Merge "Add isKtPrivate as top level kotlin class can be private when java can't" into androidx-main am: 0085fb8f48
Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2601751
Change-Id: Ie44a52015fd6e8c717e7407adc8d171ff1f2c741
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index 22ecc41..636c36c 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -85,7 +85,10 @@
public abstract class OnBackPressedCallback {
ctor public OnBackPressedCallback(boolean enabled);
+ method @MainThread @RequiresApi(34) public void handleOnBackCancelled();
method @MainThread public abstract void handleOnBackPressed();
+ method @MainThread @RequiresApi(34) public void handleOnBackProgressed(android.window.BackEvent backEvent);
+ method @MainThread @RequiresApi(34) public void handleOnBackStarted(android.window.BackEvent backEvent);
method @MainThread public final boolean isEnabled();
method @MainThread public final void remove();
method @MainThread public final void setEnabled(boolean);
@@ -95,8 +98,12 @@
public final class OnBackPressedDispatcher {
ctor public OnBackPressedDispatcher();
ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
+ ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackCancelled();
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackProgressed(android.window.BackEvent backEvent);
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackStarted(android.window.BackEvent backEvent);
method @MainThread public boolean hasEnabledCallbacks();
method @MainThread public void onBackPressed();
method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index 10c4134..94f0f54 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -84,7 +84,10 @@
public abstract class OnBackPressedCallback {
ctor public OnBackPressedCallback(boolean enabled);
+ method @MainThread @RequiresApi(34) public void handleOnBackCancelled();
method @MainThread public abstract void handleOnBackPressed();
+ method @MainThread @RequiresApi(34) public void handleOnBackProgressed(android.window.BackEvent backEvent);
+ method @MainThread @RequiresApi(34) public void handleOnBackStarted(android.window.BackEvent backEvent);
method @MainThread public final boolean isEnabled();
method @MainThread public final void remove();
method @MainThread public final void setEnabled(boolean);
@@ -94,8 +97,12 @@
public final class OnBackPressedDispatcher {
ctor public OnBackPressedDispatcher();
ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
+ ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackCancelled();
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackProgressed(android.window.BackEvent backEvent);
+ method @MainThread @RequiresApi(34) @VisibleForTesting public void dispatchOnBackStarted(android.window.BackEvent backEvent);
method @MainThread public boolean hasEnabledCallbacks();
method @MainThread public void onBackPressed();
method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
diff --git a/activity/activity/lint-baseline.xml b/activity/activity/lint-baseline.xml
index d97aee4..08512e7 100644
--- a/activity/activity/lint-baseline.xml
+++ b/activity/activity/lint-baseline.xml
@@ -10,4 +10,13 @@
file="src/main/java/androidx/activity/ComponentActivity.java"/>
</issue>
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" onBackInvokedCallback = if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/OnBackPressedDispatcher.kt"/>
+ </issue>
+
</issues>
diff --git a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
index 1ff31b7..47717b5 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherInvokerTest.kt
@@ -17,6 +17,8 @@
package androidx.activity
import android.os.Build
+import android.window.BackEvent
+import android.window.BackEvent.EDGE_LEFT
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.annotation.RequiresApi
@@ -180,4 +182,59 @@
assertThat(unregisterCount).isEqualTo(1)
}
+
+ @Test
+ @RequiresApi(34)
+ @SdkSuppress(minSdkVersion = 34)
+ fun testSimpleAnimatedCallback() {
+ var registerCount = 0
+ var unregisterCount = 0
+ val invoker = object : OnBackInvokedDispatcher {
+ override fun registerOnBackInvokedCallback(p0: Int, p1: OnBackInvokedCallback) {
+ registerCount++
+ }
+
+ override fun unregisterOnBackInvokedCallback(p0: OnBackInvokedCallback) {
+ unregisterCount++
+ }
+ }
+
+ val dispatcher = OnBackPressedDispatcher()
+
+ dispatcher.setOnBackInvokedDispatcher(invoker)
+
+ var startedCount = 0
+ var progressedCount = 0
+ var cancelledCount = 0
+ val callback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackStarted(backEvent: BackEvent) {
+ startedCount++
+ }
+
+ override fun handleOnBackProgressed(backEvent: BackEvent) {
+ progressedCount++
+ }
+ override fun handleOnBackPressed() { }
+ override fun handleOnBackCancelled() {
+ cancelledCount++
+ }
+ }
+
+ dispatcher.addCallback(callback)
+
+ assertThat(registerCount).isEqualTo(1)
+
+ dispatcher.dispatchOnBackStarted(BackEvent(0.1F, 0.1F, 0.1F, EDGE_LEFT))
+ assertThat(startedCount).isEqualTo(1)
+
+ dispatcher.dispatchOnBackProgressed(BackEvent(0.1F, 0.1F, 0.1F, EDGE_LEFT))
+ assertThat(progressedCount).isEqualTo(1)
+
+ dispatcher.dispatchOnBackCancelled()
+ assertThat(cancelledCount).isEqualTo(1)
+
+ callback.remove()
+
+ assertThat(unregisterCount).isEqualTo(1)
+ }
}
diff --git a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherTest.kt b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherTest.kt
index 51cf3de..afbe0ea 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/OnBackPressedDispatcherTest.kt
@@ -441,6 +441,80 @@
withActivity { realDispatcher.onBackPressed() }
}
}
+
+ @Test
+ fun testOnHasEnabledCallbacks() {
+ var reportedHasEnabledCallbacks = false
+ var reportCount = 0
+ val dispatcher = OnBackPressedDispatcher(
+ fallbackOnBackPressed = null,
+ onHasEnabledCallbacksChanged = {
+ reportedHasEnabledCallbacks = it
+ reportCount++
+ }
+ )
+
+ assertWithMessage("initial reportCount")
+ .that(reportCount)
+ .isEqualTo(0)
+ assertWithMessage("initial reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isFalse()
+
+ val callbackA = dispatcher.addCallback(enabled = false) {}
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(0)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isFalse()
+
+ callbackA.isEnabled = true
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(1)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isTrue()
+
+ val callbackB = dispatcher.addCallback {}
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(1)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isTrue()
+
+ callbackA.remove()
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(1)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isTrue()
+
+ callbackB.remove()
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(2)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isFalse()
+
+ dispatcher.addCallback {}
+
+ assertWithMessage("reportCount")
+ .that(reportCount)
+ .isEqualTo(3)
+ assertWithMessage("reportedHasEnabledCallbacks")
+ .that(reportedHasEnabledCallbacks)
+ .isTrue()
+ }
}
open class CountingOnBackPressedCallback(
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt b/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt
index 3101ef7..bc4b001 100644
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt
@@ -15,7 +15,9 @@
*/
package androidx.activity
+import android.window.BackEvent
import androidx.annotation.MainThread
+import androidx.annotation.RequiresApi
import java.util.concurrent.CopyOnWriteArrayList
/**
@@ -67,11 +69,38 @@
fun remove() = cancellables.forEach { it.cancel() }
/**
+ * Callback for handling the system UI generated equivalent to
+ * [OnBackPressedDispatcher.dispatchOnBackStarted].
+ */
+ @Suppress("CallbackMethodName") /* mirror handleOnBackPressed local style */
+ @RequiresApi(34)
+ @MainThread
+ open fun handleOnBackStarted(backEvent: BackEvent) {}
+
+ /**
+ * Callback for handling the system UI generated equivalent to
+ * [OnBackPressedDispatcher.dispatchOnBackProgressed].
+ */
+ @Suppress("CallbackMethodName") /* mirror handleOnBackPressed local style */
+ @RequiresApi(34)
+ @MainThread
+ open fun handleOnBackProgressed(backEvent: BackEvent) {}
+
+ /**
* Callback for handling the [OnBackPressedDispatcher.onBackPressed] event.
*/
@MainThread
abstract fun handleOnBackPressed()
+ /**
+ * Callback for handling the system UI generated equivalent to
+ * [OnBackPressedDispatcher.dispatchOnBackCancelled].
+ */
+ @Suppress("CallbackMethodName") /* mirror handleOnBackPressed local style */
+ @RequiresApi(34)
+ @MainThread
+ open fun handleOnBackCancelled() {}
+
@JvmName("addCancellable")
internal fun addCancellable(cancellable: Cancellable) {
cancellables.add(cancellable)
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
index 5319d9e..0c97526 100644
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
@@ -16,11 +16,18 @@
package androidx.activity
import android.os.Build
+import android.window.BackEvent
+import android.window.OnBackAnimationCallback
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.annotation.DoNotInline
import androidx.annotation.MainThread
+import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.core.os.BuildCompat
+import androidx.core.os.BuildCompat.PrereleaseSdkCheck
+import androidx.core.util.Consumer
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
@@ -51,14 +58,26 @@
* When constructing an instance of this class, the [fallbackOnBackPressed] can be set to
* receive a callback if [onBackPressed] is called when [hasEnabledCallbacks] returns `false`.
*/
-class OnBackPressedDispatcher @JvmOverloads constructor(
- private val fallbackOnBackPressed: Runnable? = null
+// Implementation/API compatibility note: previous releases included only the Runnable? constructor,
+// which permitted both first-argument and trailing lambda call syntax to specify
+// fallbackOnBackPressed. To avoid silently breaking source compatibility the new
+// primary constructor has no optional parameters to avoid ambiguity/wrong overload resolution
+// when a single parameter is provided as a trailing lambda.
+@OptIn(PrereleaseSdkCheck::class)
+class OnBackPressedDispatcher constructor(
+ private val fallbackOnBackPressed: Runnable?,
+ private val onHasEnabledCallbacksChanged: Consumer<Boolean>?
) {
private val onBackPressedCallbacks = ArrayDeque<OnBackPressedCallback>()
- private var enabledChangedCallback: (() -> Unit)? = null
private var onBackInvokedCallback: OnBackInvokedCallback? = null
private var invokedDispatcher: OnBackInvokedDispatcher? = null
private var backInvokedCallbackRegistered = false
+ private var hasEnabledCallbacks = false
+
+ @JvmOverloads
+ constructor(
+ fallbackOnBackPressed: Runnable? = null
+ ) : this(fallbackOnBackPressed, null)
/**
* Sets the [OnBackInvokedDispatcher] for handling system back for Android SDK T+.
@@ -68,12 +87,11 @@
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun setOnBackInvokedDispatcher(invoker: OnBackInvokedDispatcher) {
invokedDispatcher = invoker
- updateBackInvokedCallbackState()
+ updateBackInvokedCallbackState(hasEnabledCallbacks)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
- internal fun updateBackInvokedCallbackState() {
- val shouldBeRegistered = hasEnabledCallbacks()
+ private fun updateBackInvokedCallbackState(shouldBeRegistered: Boolean) {
val dispatcher = invokedDispatcher
val onBackInvokedCallback = onBackInvokedCallback
if (dispatcher != null && onBackInvokedCallback != null) {
@@ -94,12 +112,30 @@
}
}
+ private fun updateEnabledCallbacks() {
+ val hadEnabledCallbacks = hasEnabledCallbacks
+ val hasEnabledCallbacks = onBackPressedCallbacks.any { it.isEnabled }
+ this.hasEnabledCallbacks = hasEnabledCallbacks
+ if (hasEnabledCallbacks != hadEnabledCallbacks) {
+ onHasEnabledCallbacksChanged?.accept(hasEnabledCallbacks)
+ if (Build.VERSION.SDK_INT >= 33) {
+ updateBackInvokedCallbackState(hasEnabledCallbacks)
+ }
+ }
+ }
+
init {
if (Build.VERSION.SDK_INT >= 33) {
- enabledChangedCallback = {
- updateBackInvokedCallbackState()
+ onBackInvokedCallback = if (BuildCompat.isAtLeastU()) {
+ Api34Impl.createOnBackAnimationCallback(
+ { backEvent -> onBackStarted(backEvent) },
+ { backEvent -> onBackProgressed(backEvent) },
+ { onBackPressed() },
+ { onBackCancelled() }
+ )
+ } else {
+ Api33Impl.createOnBackInvokedCallback { onBackPressed() }
}
- onBackInvokedCallback = Api33Impl.createOnBackInvokedCallback { onBackPressed() }
}
}
@@ -137,10 +173,8 @@
onBackPressedCallbacks.add(onBackPressedCallback)
val cancellable = OnBackPressedCancellable(onBackPressedCallback)
onBackPressedCallback.addCancellable(cancellable)
- if (Build.VERSION.SDK_INT >= 33) {
- updateBackInvokedCallbackState()
- onBackPressedCallback.enabledChangedCallback = enabledChangedCallback
- }
+ updateEnabledCallbacks()
+ onBackPressedCallback.enabledChangedCallback = ::updateEnabledCallbacks
return cancellable
}
@@ -178,21 +212,55 @@
onBackPressedCallback.addCancellable(
LifecycleOnBackPressedCancellable(lifecycle, onBackPressedCallback)
)
- if (Build.VERSION.SDK_INT >= 33) {
- updateBackInvokedCallbackState()
- onBackPressedCallback.enabledChangedCallback = enabledChangedCallback
- }
+ updateEnabledCallbacks()
+ onBackPressedCallback.enabledChangedCallback = ::updateEnabledCallbacks
}
/**
- * Checks if there is at least one [enabled][OnBackPressedCallback.isEnabled]
+ * Returns `true` if there is at least one [enabled][OnBackPressedCallback.isEnabled]
* callback registered with this dispatcher.
*
* @return True if there is at least one enabled callback.
*/
@MainThread
- fun hasEnabledCallbacks(): Boolean = onBackPressedCallbacks.any {
- it.isEnabled
+ fun hasEnabledCallbacks(): Boolean = hasEnabledCallbacks
+
+ @VisibleForTesting
+ @RequiresApi(34)
+ @MainThread
+ fun dispatchOnBackStarted(backEvent: BackEvent) {
+ onBackStarted(backEvent)
+ }
+
+ @RequiresApi(34)
+ @MainThread
+ private fun onBackStarted(backEvent: BackEvent) {
+ val callback = onBackPressedCallbacks.lastOrNull {
+ it.isEnabled
+ }
+ if (callback != null) {
+ callback.handleOnBackStarted(backEvent)
+ return
+ }
+ }
+
+ @VisibleForTesting
+ @RequiresApi(34)
+ @MainThread
+ fun dispatchOnBackProgressed(backEvent: BackEvent) {
+ onBackProgressed(backEvent)
+ }
+
+ @RequiresApi(34)
+ @MainThread
+ private fun onBackProgressed(backEvent: BackEvent) {
+ val callback = onBackPressedCallbacks.lastOrNull {
+ it.isEnabled
+ }
+ if (callback != null) {
+ callback.handleOnBackProgressed(backEvent)
+ return
+ }
}
/**
@@ -216,16 +284,33 @@
fallbackOnBackPressed?.run()
}
+ @VisibleForTesting
+ @RequiresApi(34)
+ @MainThread
+ fun dispatchOnBackCancelled() {
+ onBackCancelled()
+ }
+
+ @RequiresApi(34)
+ @MainThread
+ private fun onBackCancelled() {
+ val callback = onBackPressedCallbacks.lastOrNull {
+ it.isEnabled
+ }
+ if (callback != null) {
+ callback.handleOnBackCancelled()
+ return
+ }
+ }
+
private inner class OnBackPressedCancellable(
private val onBackPressedCallback: OnBackPressedCallback
) : Cancellable {
override fun cancel() {
onBackPressedCallbacks.remove(onBackPressedCallback)
onBackPressedCallback.removeCancellable(this)
- if (Build.VERSION.SDK_INT >= 33) {
- onBackPressedCallback.enabledChangedCallback = null
- updateBackInvokedCallbackState()
- }
+ onBackPressedCallback.enabledChangedCallback?.invoke()
+ onBackPressedCallback.enabledChangedCallback = null
}
}
@@ -286,6 +371,35 @@
return OnBackInvokedCallback { onBackInvoked() }
}
}
+
+ @RequiresApi(34)
+ internal object Api34Impl {
+ @DoNotInline
+ fun createOnBackAnimationCallback(
+ onBackStarted: (backEvent: BackEvent) -> Unit,
+ onBackProgressed: (backEvent: BackEvent) -> Unit,
+ onBackInvoked: () -> Unit,
+ onBackCancelled: () -> Unit
+ ): OnBackInvokedCallback {
+ return object : OnBackAnimationCallback {
+ override fun onBackStarted(backEvent: BackEvent) {
+ onBackStarted(backEvent)
+ }
+
+ override fun onBackProgressed(backEvent: BackEvent) {
+ onBackProgressed(backEvent)
+ }
+
+ override fun onBackInvoked() {
+ onBackInvoked()
+ }
+
+ override fun onBackCancelled() {
+ onBackCancelled()
+ }
+ }
+ }
+ }
}
/**
diff --git a/appactions/interaction/interaction-service/src/test/resources/robolectric.properties b/appactions/interaction/interaction-service/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/appactions/interaction/interaction-service/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/appcompat/appcompat-resources/api/api_lint.ignore b/appcompat/appcompat-resources/api/api_lint.ignore
index 0cfa261..dd0cf8d 100644
--- a/appcompat/appcompat-resources/api/api_lint.ignore
+++ b/appcompat/appcompat-resources/api/api_lint.ignore
@@ -17,6 +17,8 @@
Missing nullability on parameter `tint` in method `setTintList`
MissingNullability: androidx.appcompat.graphics.drawable.DrawableWrapperCompat#DrawableWrapperCompat(android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `drawable` in method `DrawableWrapperCompat`
+MissingNullability: androidx.appcompat.graphics.drawable.DrawableWrapperCompat#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.appcompat.graphics.drawable.DrawableWrapperCompat#getCurrent():
Missing nullability on method `getCurrent` return
MissingNullability: androidx.appcompat.graphics.drawable.DrawableWrapperCompat#getPadding(android.graphics.Rect) parameter #0:
diff --git a/appcompat/appcompat/api/api_lint.ignore b/appcompat/appcompat/api/api_lint.ignore
index ff1d34f..90541cfd 100644
--- a/appcompat/appcompat/api/api_lint.ignore
+++ b/appcompat/appcompat/api/api_lint.ignore
@@ -91,12 +91,8 @@
Invalid nullability on parameter `filters` in method `setFilters`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.appcompat.widget.AppCompatToggleButton#setFilters(android.text.InputFilter[]) parameter #0:
Invalid nullability on parameter `filters` in method `setFilters`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.appcompat.widget.LinearLayoutCompat#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.appcompat.widget.ListPopupWindow#getListView():
Invalid nullability on method `getListView` return. Overrides of unannotated super method cannot be Nullable.
-InvalidNullabilityOverride: androidx.appcompat.widget.SwitchCompat#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.appcompat.widget.SwitchCompat#getCustomSelectionActionModeCallback():
Invalid nullability on method `getCustomSelectionActionModeCallback` return. Overrides of unannotated super method cannot be Nullable.
InvalidNullabilityOverride: androidx.appcompat.widget.SwitchCompat#setFilters(android.text.InputFilter[]) parameter #0:
@@ -555,6 +551,8 @@
Missing nullability on parameter `attrs` in method `createView`
MissingNullability: androidx.appcompat.graphics.drawable.DrawerArrowDrawable#DrawerArrowDrawable(android.content.Context) parameter #0:
Missing nullability on parameter `context` in method `DrawerArrowDrawable`
+MissingNullability: androidx.appcompat.graphics.drawable.DrawerArrowDrawable#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.appcompat.graphics.drawable.DrawerArrowDrawable#getPaint():
Missing nullability on method `getPaint` return
MissingNullability: androidx.appcompat.graphics.drawable.DrawerArrowDrawable#setColorFilter(android.graphics.ColorFilter) parameter #0:
@@ -727,6 +725,8 @@
Missing nullability on parameter `p` in method `generateLayoutParams`
MissingNullability: androidx.appcompat.widget.LinearLayoutCompat#getDividerDrawable():
Missing nullability on method `getDividerDrawable` return
+MissingNullability: androidx.appcompat.widget.LinearLayoutCompat#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.appcompat.widget.LinearLayoutCompat#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent) parameter #0:
Missing nullability on parameter `event` in method `onInitializeAccessibilityEvent`
MissingNullability: androidx.appcompat.widget.LinearLayoutCompat#onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo) parameter #0:
@@ -797,6 +797,8 @@
Missing nullability on parameter `source` in method `onShareTargetSelected`
MissingNullability: androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener#onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider, android.content.Intent) parameter #1:
Missing nullability on parameter `intent` in method `onShareTargetSelected`
+MissingNullability: androidx.appcompat.widget.SwitchCompat#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `draw`
MissingNullability: androidx.appcompat.widget.SwitchCompat#getTextOff():
Missing nullability on method `getTextOff` return
MissingNullability: androidx.appcompat.widget.SwitchCompat#getTextOn():
@@ -831,6 +833,8 @@
Missing nullability on parameter `thumb` in method `setThumbDrawable`
MissingNullability: androidx.appcompat.widget.SwitchCompat#setTrackDrawable(android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `track` in method `setTrackDrawable`
+MissingNullability: androidx.appcompat.widget.SwitchCompat#verifyDrawable(android.graphics.drawable.Drawable) parameter #0:
+ Missing nullability on parameter `who` in method `verifyDrawable`
MissingNullability: androidx.appcompat.widget.Toolbar#checkLayoutParams(android.view.ViewGroup.LayoutParams) parameter #0:
Missing nullability on parameter `p` in method `checkLayoutParams`
MissingNullability: androidx.appcompat.widget.Toolbar#generateDefaultLayoutParams():
diff --git a/appcompat/appcompat/lint-baseline.xml b/appcompat/appcompat/lint-baseline.xml
index dc8d901..61c19d7 100644
--- a/appcompat/appcompat/lint-baseline.xml
+++ b/appcompat/appcompat/lint-baseline.xml
@@ -3448,6 +3448,15 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected synchronized void onDraw(Canvas canvas) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"/>
+ </issue>
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" int defStyleAttr, int mode, Resources.Theme popupTheme) {"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
@@ -7111,6 +7120,15 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected void onDraw(Canvas canvas) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"/>
+ </issue>
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public static TintTypedArray obtainStyledAttributes(Context context, AttributeSet set,"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -7966,6 +7984,15 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected void dispatchDraw(Canvas canvas) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"/>
+ </issue>
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public View inflate() {"
errorLine2=" ~~~~">
<location
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesActivityA.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesActivityA.java
index 069c76c..5719451 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesActivityA.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesActivityA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 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.
@@ -20,4 +20,3 @@
* An activity for locales with a unique class name.
*/
public class LocalesActivityA extends LocalesUpdateActivity {}
-
diff --git a/appintegration/OWNERS b/appintegration/OWNERS
new file mode 100644
index 0000000..c29ded6
--- /dev/null
+++ b/appintegration/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1293429
+yim@google.com
diff --git a/appsearch/appsearch-builtin-types/api/current.txt b/appsearch/appsearch-builtin-types/api/current.txt
index a3244de..1e295d6 100644
--- a/appsearch/appsearch-builtin-types/api/current.txt
+++ b/appsearch/appsearch-builtin-types/api/current.txt
@@ -29,8 +29,10 @@
ctor public Alarm.Builder(androidx.appsearch.builtintypes.Alarm);
ctor public Alarm.Builder(String, String);
method public androidx.appsearch.builtintypes.Alarm.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Alarm.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Alarm build();
method public androidx.appsearch.builtintypes.Alarm.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Alarm.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodEndDate(String?);
method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodStartDate(String?);
method public androidx.appsearch.builtintypes.Alarm.Builder setCreationTimestampMillis(long);
@@ -66,8 +68,10 @@
ctor public AlarmInstance.Builder(androidx.appsearch.builtintypes.AlarmInstance);
ctor public AlarmInstance.Builder(String, String, String);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.AlarmInstance.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.AlarmInstance build();
method public androidx.appsearch.builtintypes.AlarmInstance.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.AlarmInstance.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setDocumentScore(int);
@@ -90,8 +94,10 @@
ctor public ContactPoint.Builder(androidx.appsearch.builtintypes.ContactPoint);
ctor public ContactPoint.Builder(String, String, String);
method public androidx.appsearch.builtintypes.ContactPoint.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.ContactPoint.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.ContactPoint build();
method public androidx.appsearch.builtintypes.ContactPoint.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.ContactPoint.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.ContactPoint.Builder setAddresses(java.util.List<java.lang.String!>);
method public androidx.appsearch.builtintypes.ContactPoint.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.ContactPoint.Builder setDescription(String?);
@@ -117,8 +123,10 @@
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeyword(androidx.appsearch.builtintypes.properties.Keyword);
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeyword(String);
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeywords(Iterable<androidx.appsearch.builtintypes.properties.Keyword!>);
+ method public androidx.appsearch.builtintypes.ImageObject.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.ImageObject build();
method public androidx.appsearch.builtintypes.ImageObject.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.ImageObject.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.ImageObject.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.ImageObject.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.ImageObject.Builder setDocumentScore(int);
@@ -159,8 +167,10 @@
ctor public Person.Builder(androidx.appsearch.builtintypes.Person);
ctor public Person.Builder(String, String, String);
method public androidx.appsearch.builtintypes.Person.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Person.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Person build();
method public androidx.appsearch.builtintypes.Person.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Person.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Person.Builder setAdditionalNames(java.util.List<androidx.appsearch.builtintypes.Person.AdditionalName!>);
method public androidx.appsearch.builtintypes.Person.Builder setAffiliations(java.util.List<java.lang.String!>);
method public androidx.appsearch.builtintypes.Person.Builder setBot(boolean);
@@ -182,6 +192,21 @@
method public androidx.appsearch.builtintypes.Person.Builder setUrl(String?);
}
+ @androidx.appsearch.annotation.Document(name="builtin:PotentialAction") public class PotentialAction {
+ method public String? getDescription();
+ method public String? getName();
+ method public String? getUri();
+ }
+
+ public static final class PotentialAction.Builder {
+ ctor public PotentialAction.Builder();
+ ctor public PotentialAction.Builder(androidx.appsearch.builtintypes.PotentialAction);
+ method public androidx.appsearch.builtintypes.PotentialAction build();
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setDescription(String?);
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setName(String?);
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setUri(String?);
+ }
+
@androidx.appsearch.annotation.Document(name="builtin:Stopwatch") public class Stopwatch extends androidx.appsearch.builtintypes.Thing {
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public long calculateBaseTimeMillis(android.content.Context);
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public long calculateCurrentAccumulatedDurationMillis(android.content.Context);
@@ -201,8 +226,10 @@
ctor public Stopwatch.Builder(androidx.appsearch.builtintypes.Stopwatch);
ctor public Stopwatch.Builder(String, String);
method public androidx.appsearch.builtintypes.Stopwatch.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Stopwatch.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Stopwatch build();
method public androidx.appsearch.builtintypes.Stopwatch.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Stopwatch.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Stopwatch.Builder setAccumulatedDurationMillis(long);
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public androidx.appsearch.builtintypes.Stopwatch.Builder setBaseTimeMillis(android.content.Context, long, long);
method public androidx.appsearch.builtintypes.Stopwatch.Builder setBaseTimeMillis(long, long, int);
@@ -227,8 +254,10 @@
ctor public StopwatchLap.Builder(androidx.appsearch.builtintypes.StopwatchLap);
ctor public StopwatchLap.Builder(String, String);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.StopwatchLap.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.StopwatchLap build();
method public androidx.appsearch.builtintypes.StopwatchLap.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.StopwatchLap.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setAccumulatedLapDurationMillis(long);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setDescription(String?);
@@ -251,6 +280,7 @@
method public String? getImage();
method public String? getName();
method public String getNamespace();
+ method public java.util.List<androidx.appsearch.builtintypes.PotentialAction!> getPotentialActions();
method public String? getUrl();
}
@@ -258,8 +288,10 @@
ctor public Thing.Builder(androidx.appsearch.builtintypes.Thing);
ctor public Thing.Builder(String, String);
method public androidx.appsearch.builtintypes.Thing.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Thing.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Thing build();
method public androidx.appsearch.builtintypes.Thing.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Thing.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Thing.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.Thing.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.Thing.Builder setDocumentScore(int);
@@ -295,8 +327,10 @@
ctor public Timer.Builder(androidx.appsearch.builtintypes.Timer);
ctor public Timer.Builder(String, String);
method public androidx.appsearch.builtintypes.Timer.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Timer.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Timer build();
method public androidx.appsearch.builtintypes.Timer.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Timer.Builder clearPotentialActions();
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public androidx.appsearch.builtintypes.Timer.Builder setBaseTimeMillis(android.content.Context, long, long);
method public androidx.appsearch.builtintypes.Timer.Builder setBaseTimeMillis(long, long, int);
method public androidx.appsearch.builtintypes.Timer.Builder setCreationTimestampMillis(long);
diff --git a/appsearch/appsearch-builtin-types/api/restricted_current.txt b/appsearch/appsearch-builtin-types/api/restricted_current.txt
index a9939c7..e402ed8 100644
--- a/appsearch/appsearch-builtin-types/api/restricted_current.txt
+++ b/appsearch/appsearch-builtin-types/api/restricted_current.txt
@@ -31,8 +31,10 @@
ctor public Alarm.Builder(androidx.appsearch.builtintypes.Alarm);
ctor public Alarm.Builder(String, String);
method public androidx.appsearch.builtintypes.Alarm.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Alarm.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Alarm build();
method public androidx.appsearch.builtintypes.Alarm.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Alarm.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodEndDate(String?);
method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodStartDate(String?);
method public androidx.appsearch.builtintypes.Alarm.Builder setCreationTimestampMillis(long);
@@ -68,8 +70,10 @@
ctor public AlarmInstance.Builder(androidx.appsearch.builtintypes.AlarmInstance);
ctor public AlarmInstance.Builder(String, String, String);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.AlarmInstance.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.AlarmInstance build();
method public androidx.appsearch.builtintypes.AlarmInstance.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.AlarmInstance.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.AlarmInstance.Builder setDocumentScore(int);
@@ -92,8 +96,10 @@
ctor public ContactPoint.Builder(androidx.appsearch.builtintypes.ContactPoint);
ctor public ContactPoint.Builder(String, String, String);
method public androidx.appsearch.builtintypes.ContactPoint.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.ContactPoint.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.ContactPoint build();
method public androidx.appsearch.builtintypes.ContactPoint.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.ContactPoint.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.ContactPoint.Builder setAddresses(java.util.List<java.lang.String!>);
method public androidx.appsearch.builtintypes.ContactPoint.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.ContactPoint.Builder setDescription(String?);
@@ -119,8 +125,10 @@
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeyword(androidx.appsearch.builtintypes.properties.Keyword);
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeyword(String);
method public androidx.appsearch.builtintypes.ImageObject.Builder addKeywords(Iterable<androidx.appsearch.builtintypes.properties.Keyword!>);
+ method public androidx.appsearch.builtintypes.ImageObject.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.ImageObject build();
method public androidx.appsearch.builtintypes.ImageObject.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.ImageObject.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.ImageObject.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.ImageObject.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.ImageObject.Builder setDocumentScore(int);
@@ -161,8 +169,10 @@
ctor public Person.Builder(androidx.appsearch.builtintypes.Person);
ctor public Person.Builder(String, String, String);
method public androidx.appsearch.builtintypes.Person.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Person.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Person build();
method public androidx.appsearch.builtintypes.Person.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Person.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Person.Builder setAdditionalNames(java.util.List<androidx.appsearch.builtintypes.Person.AdditionalName!>);
method public androidx.appsearch.builtintypes.Person.Builder setAffiliations(java.util.List<java.lang.String!>);
method public androidx.appsearch.builtintypes.Person.Builder setBot(boolean);
@@ -184,6 +194,21 @@
method public androidx.appsearch.builtintypes.Person.Builder setUrl(String?);
}
+ @androidx.appsearch.annotation.Document(name="builtin:PotentialAction") public class PotentialAction {
+ method public String? getDescription();
+ method public String? getName();
+ method public String? getUri();
+ }
+
+ public static final class PotentialAction.Builder {
+ ctor public PotentialAction.Builder();
+ ctor public PotentialAction.Builder(androidx.appsearch.builtintypes.PotentialAction);
+ method public androidx.appsearch.builtintypes.PotentialAction build();
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setDescription(String?);
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setName(String?);
+ method public androidx.appsearch.builtintypes.PotentialAction.Builder setUri(String?);
+ }
+
@androidx.appsearch.annotation.Document(name="builtin:Stopwatch") public class Stopwatch extends androidx.appsearch.builtintypes.Thing {
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public long calculateBaseTimeMillis(android.content.Context);
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public long calculateCurrentAccumulatedDurationMillis(android.content.Context);
@@ -203,8 +228,10 @@
ctor public Stopwatch.Builder(androidx.appsearch.builtintypes.Stopwatch);
ctor public Stopwatch.Builder(String, String);
method public androidx.appsearch.builtintypes.Stopwatch.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Stopwatch.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Stopwatch build();
method public androidx.appsearch.builtintypes.Stopwatch.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Stopwatch.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Stopwatch.Builder setAccumulatedDurationMillis(long);
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public androidx.appsearch.builtintypes.Stopwatch.Builder setBaseTimeMillis(android.content.Context, long, long);
method public androidx.appsearch.builtintypes.Stopwatch.Builder setBaseTimeMillis(long, long, int);
@@ -229,8 +256,10 @@
ctor public StopwatchLap.Builder(androidx.appsearch.builtintypes.StopwatchLap);
ctor public StopwatchLap.Builder(String, String);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.StopwatchLap.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.StopwatchLap build();
method public androidx.appsearch.builtintypes.StopwatchLap.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.StopwatchLap.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setAccumulatedLapDurationMillis(long);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.StopwatchLap.Builder setDescription(String?);
@@ -253,6 +282,7 @@
method public String? getImage();
method public String? getName();
method public String getNamespace();
+ method public java.util.List<androidx.appsearch.builtintypes.PotentialAction!> getPotentialActions();
method public String? getUrl();
}
@@ -260,8 +290,10 @@
ctor public Thing.Builder(androidx.appsearch.builtintypes.Thing);
ctor public Thing.Builder(String, String);
method public androidx.appsearch.builtintypes.Thing.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Thing.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Thing build();
method public androidx.appsearch.builtintypes.Thing.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Thing.Builder clearPotentialActions();
method public androidx.appsearch.builtintypes.Thing.Builder setCreationTimestampMillis(long);
method public androidx.appsearch.builtintypes.Thing.Builder setDescription(String?);
method public androidx.appsearch.builtintypes.Thing.Builder setDocumentScore(int);
@@ -297,8 +329,10 @@
ctor public Timer.Builder(androidx.appsearch.builtintypes.Timer);
ctor public Timer.Builder(String, String);
method public androidx.appsearch.builtintypes.Timer.Builder addAlternateName(String);
+ method public androidx.appsearch.builtintypes.Timer.Builder addPotentialAction(androidx.appsearch.builtintypes.PotentialAction);
method public androidx.appsearch.builtintypes.Timer build();
method public androidx.appsearch.builtintypes.Timer.Builder clearAlternateNames();
+ method public androidx.appsearch.builtintypes.Timer.Builder clearPotentialActions();
method @RequiresApi(api=android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) public androidx.appsearch.builtintypes.Timer.Builder setBaseTimeMillis(android.content.Context, long, long);
method public androidx.appsearch.builtintypes.Timer.Builder setBaseTimeMillis(long, long, int);
method public androidx.appsearch.builtintypes.Timer.Builder setCreationTimestampMillis(long);
diff --git a/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/PotentialActionTest.java b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/PotentialActionTest.java
new file mode 100644
index 0000000..a0117d7
--- /dev/null
+++ b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/PotentialActionTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.builtintypes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.GenericDocument;
+
+import org.junit.Test;
+
+public class PotentialActionTest {
+ @Test
+ public void testBuilder() {
+ PotentialAction potentialAction = new PotentialAction.Builder()
+ .setName("actions.intent.CREATE_CALL")
+ .setDescription("Call John")
+ .build();
+
+ assertThat(potentialAction.getName()).isEqualTo("actions.intent.CREATE_CALL");
+ assertThat(potentialAction.getDescription()).isEqualTo("Call John");
+ }
+
+ @Test
+ public void testBuilderCopy_returnsActionWithAllFieldsCopied() {
+ PotentialAction potentialAction1 = new PotentialAction.Builder()
+ .setName("actions.intent.CREATE_CALL")
+ .setDescription("Call John")
+ .build();
+
+ PotentialAction potentialAction2 = new PotentialAction.Builder(potentialAction1).build();
+ assertThat(potentialAction1.getName()).isEqualTo(potentialAction2.getName());
+ assertThat(potentialAction1.getDescription()).isEqualTo(potentialAction2.getDescription());
+ assertThat(potentialAction1.getUri()).isEqualTo(potentialAction2.getUri());
+ }
+
+ @Test
+ public void testActionToGenericDocument() throws Exception {
+ PotentialAction potentialAction = new PotentialAction.Builder()
+ .setName("actions.intent.CREATE_CALL")
+ .setDescription("Call John")
+ .setUri("tel:555-123-4567")
+ .build();
+
+ GenericDocument genericDocument = GenericDocument.fromDocumentClass(potentialAction);
+ assertThat(genericDocument.getSchemaType()).isEqualTo("builtin:PotentialAction");
+ assertThat(genericDocument.getPropertyString("name"))
+ .isEqualTo("actions.intent.CREATE_CALL");
+ assertThat(genericDocument.getPropertyString("description"))
+ .isEqualTo("Call John");
+ assertThat(genericDocument.getPropertyString("uri"))
+ .isEqualTo("tel:555-123-4567");
+ }
+}
diff --git a/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/ThingTest.java b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/ThingTest.java
index 9093ced..92198a9 100644
--- a/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/ThingTest.java
+++ b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/ThingTest.java
@@ -23,9 +23,9 @@
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
public class ThingTest {
-
@Test
public void testBuilder() {
long now = System.currentTimeMillis();
@@ -39,6 +39,16 @@
.setDescription("this is my first schema.org object")
.setImage("content://images/thing1")
.setUrl("content://things/1")
+ .addPotentialAction(new PotentialAction.Builder()
+ .setName("Start Action")
+ .setDescription("Starts the thing")
+ .setUri("package://start")
+ .build())
+ .addPotentialAction(new PotentialAction.Builder()
+ .setName("Stop Action")
+ .setDescription("Stops the thing")
+ .setUri("package://stop")
+ .build())
.build();
assertThat(thing.getNamespace()).isEqualTo("namespace");
@@ -53,6 +63,17 @@
assertThat(thing.getDescription()).isEqualTo("this is my first schema.org object");
assertThat(thing.getImage()).isEqualTo("content://images/thing1");
assertThat(thing.getUrl()).isEqualTo("content://things/1");
+ assertThat(thing.getPotentialActions()).hasSize(2);
+
+ PotentialAction startAction = thing.getPotentialActions().get(0);
+ assertThat(startAction.getName()).isEqualTo("Start Action");
+ assertThat(startAction.getDescription()).isEqualTo("Starts the thing");
+ assertThat(startAction.getUri()).isEqualTo("package://start");
+
+ PotentialAction stopAction = thing.getPotentialActions().get(1);
+ assertThat(stopAction.getName()).isEqualTo("Stop Action");
+ assertThat(stopAction.getDescription()).isEqualTo("Stops the thing");
+ assertThat(stopAction.getUri()).isEqualTo("package://stop");
}
@Test
@@ -68,6 +89,11 @@
.setDescription("this is my first schema.org object")
.setImage("content://images/thing1")
.setUrl("content://things/1")
+ .addPotentialAction(new PotentialAction.Builder()
+ .setName("Stop Action")
+ .setDescription("Stops the thing")
+ .setUri("package://stop")
+ .build())
.build();
Thing thing2 = new Thing.Builder(thing1).build();
@@ -83,6 +109,12 @@
assertThat(thing2.getDescription()).isEqualTo("this is my first schema.org object");
assertThat(thing2.getImage()).isEqualTo("content://images/thing1");
assertThat(thing2.getUrl()).isEqualTo("content://things/1");
+ assertThat(thing2.getPotentialActions()).isNotNull();
+ assertThat(thing2.getPotentialActions()).hasSize(1);
+ assertThat(thing2.getPotentialActions().get(0).getName()).isEqualTo("Stop Action");
+ assertThat(thing2.getPotentialActions().get(0).getDescription())
+ .isEqualTo("Stops the thing");
+ assertThat(thing2.getPotentialActions().get(0).getUri()).isEqualTo("package://stop");
}
@Test
@@ -98,11 +130,25 @@
.setDescription("this is my first schema.org object")
.setImage("content://images/thing1")
.setUrl("content://things/1")
+ .addPotentialAction(new PotentialAction.Builder()
+ .setDescription("View this thing")
+ .setUri("package://view")
+ .build())
+ .addPotentialAction(new PotentialAction.Builder()
+ .setDescription("Edit this thing")
+ .setUri("package://edit")
+ .build())
.build();
Thing thing2 = new Thing.Builder(thing1)
.clearAlternateNames()
.setImage("content://images/thing2")
.setUrl("content://things/2")
+ .clearPotentialActions()
+ .addPotentialAction(new PotentialAction.Builder()
+ .setName("DeleteAction")
+ .setDescription("Delete this thing")
+ .setUri("package://delete")
+ .build())
.build();
assertThat(thing2.getNamespace()).isEqualTo("namespace");
@@ -115,11 +161,99 @@
assertThat(thing2.getDescription()).isEqualTo("this is my first schema.org object");
assertThat(thing2.getImage()).isEqualTo("content://images/thing2");
assertThat(thing2.getUrl()).isEqualTo("content://things/2");
+
+ List<PotentialAction> potentialActions = thing2.getPotentialActions();
+ assertThat(potentialActions).hasSize(1);
+ assertThat(potentialActions.get(0).getName()).isEqualTo("DeleteAction");
+ assertThat(potentialActions.get(0).getDescription()).isEqualTo("Delete this thing");
+ assertThat(potentialActions.get(0).getUri()).isEqualTo("package://delete");
}
@Test
+ public void testBuilderCopy_builderReuse() {
+ long now = System.currentTimeMillis();
+ Thing.Builder builder = new Thing.Builder("namespace", "thing1")
+ .setDocumentScore(1)
+ .setCreationTimestampMillis(now)
+ .setDocumentTtlMillis(30000)
+ .setName("my first thing")
+ .addAlternateName("my first object")
+ .addAlternateName("माझी पहिली गोष्ट")
+ .setDescription("this is my first schema.org object")
+ .setImage("content://images/thing1")
+ .setUrl("content://things/1")
+ .addPotentialAction(new PotentialAction.Builder()
+ .setDescription("View this thing")
+ .setUri("package://view")
+ .build())
+ .addPotentialAction(new PotentialAction.Builder()
+ .setDescription("Edit this thing")
+ .setUri("package://edit")
+ .build());
+
+ Thing thing1 = builder.build();
+
+ builder.clearAlternateNames()
+ .setImage("content://images/thing2")
+ .setUrl("content://things/2")
+ .clearPotentialActions()
+ .addPotentialAction(new PotentialAction.Builder()
+ .setName("DeleteAction")
+ .setDescription("Delete this thing")
+ .setUri("package://delete")
+ .build());
+
+ Thing thing2 = builder.build();
+
+ // Check that thing1 wasn't altered
+ assertThat(thing1.getNamespace()).isEqualTo("namespace");
+ assertThat(thing1.getId()).isEqualTo("thing1");
+ assertThat(thing1.getDocumentScore()).isEqualTo(1);
+ assertThat(thing1.getCreationTimestampMillis()).isEqualTo(now);
+ assertThat(thing1.getDocumentTtlMillis()).isEqualTo(30000);
+ assertThat(thing1.getName()).isEqualTo("my first thing");
+ assertThat(thing1.getAlternateNames())
+ .containsExactly("my first object", "माझी पहिली गोष्ट");
+ assertThat(thing1.getDescription()).isEqualTo("this is my first schema.org object");
+ assertThat(thing1.getImage()).isEqualTo("content://images/thing1");
+ assertThat(thing1.getUrl()).isEqualTo("content://things/1");
+
+ List<PotentialAction> actions1 = thing1.getPotentialActions();
+ assertThat(actions1).hasSize(2);
+ assertThat(actions1.get(0).getDescription()).isEqualTo("View this thing");
+ assertThat(actions1.get(0).getUri()).isEqualTo("package://view");
+ assertThat(actions1.get(1).getDescription()).isEqualTo("Edit this thing");
+ assertThat(actions1.get(1).getUri()).isEqualTo("package://edit");
+
+ // Check that thing2 has the new values
+ assertThat(thing2.getNamespace()).isEqualTo("namespace");
+ assertThat(thing2.getId()).isEqualTo("thing1");
+ assertThat(thing2.getDocumentScore()).isEqualTo(1);
+ assertThat(thing2.getCreationTimestampMillis()).isEqualTo(now);
+ assertThat(thing2.getDocumentTtlMillis()).isEqualTo(30000);
+ assertThat(thing2.getName()).isEqualTo("my first thing");
+ assertThat(thing2.getAlternateNames()).isEmpty();
+ assertThat(thing2.getDescription()).isEqualTo("this is my first schema.org object");
+ assertThat(thing2.getImage()).isEqualTo("content://images/thing2");
+ assertThat(thing2.getUrl()).isEqualTo("content://things/2");
+
+ List<PotentialAction> actions2 = thing2.getPotentialActions();
+ assertThat(actions2).hasSize(1);
+ assertThat(actions2.get(0).getName()).isEqualTo("DeleteAction");
+ assertThat(actions2.get(0).getDescription()).isEqualTo("Delete this thing");
+ assertThat(actions2.get(0).getUri()).isEqualTo("package://delete");
+ }
+
+
+ @Test
public void testToGenericDocument() throws Exception {
long now = System.currentTimeMillis();
+ PotentialAction potentialAction = new PotentialAction.Builder()
+ .setDescription("Make a phone call")
+ .setName("actions.intent.CALL")
+ .setUri("package://call")
+ .build();
+
Thing thing = new Thing.Builder("namespace", "thing1")
.setDocumentScore(1)
.setCreationTimestampMillis(now)
@@ -130,6 +264,7 @@
.setDescription("this is my first schema.org object")
.setImage("content://images/thing1")
.setUrl("content://things/1")
+ .addPotentialAction(potentialAction)
.build();
GenericDocument document = GenericDocument.fromDocumentClass(thing);
@@ -147,5 +282,12 @@
.isEqualTo("this is my first schema.org object");
assertThat(document.getPropertyString("image")).isEqualTo("content://images/thing1");
assertThat(document.getPropertyString("url")).isEqualTo("content://things/1");
+
+ assertThat(document.getPropertyString("potentialActions[0].name"))
+ .isEqualTo("actions.intent.CALL");
+ assertThat(document.getPropertyString("potentialActions[0].description"))
+ .isEqualTo("Make a phone call");
+ assertThat(document.getPropertyString("potentialActions[0].uri"))
+ .isEqualTo("package://call");
}
}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
index da4a47a..4c4a2bb 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
@@ -65,12 +65,13 @@
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
@Nullable String image, @Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
boolean enabled, @Nullable int[] daysOfWeek, int hour, int minute,
@Nullable String blackoutPeriodStartDate, @Nullable String blackoutPeriodEndDate,
@Nullable String ringtone, boolean shouldVibrate,
@Nullable AlarmInstance previousInstance, @Nullable AlarmInstance nextInstance) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mEnabled = enabled;
mDaysOfWeek = daysOfWeek;
mHour = hour;
@@ -396,6 +397,7 @@
public Alarm build() {
return new Alarm(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions,
mEnabled, mDaysOfWeek, mHour, mMinute, mBlackoutPeriodStartDate,
mBlackoutPeriodEndDate, mRingtone, mShouldVibrate, mPreviousInstance,
mNextInstance);
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/AlarmInstance.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/AlarmInstance.java
index 59a142a..edab549 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/AlarmInstance.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/AlarmInstance.java
@@ -74,10 +74,11 @@
AlarmInstance(@NonNull String namespace, @NonNull String id, int documentScore,
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
- @Nullable String image, @Nullable String url, @NonNull String scheduledTime,
+ @Nullable String image, @Nullable String url,
+ @NonNull List<PotentialAction> potentialActions, @NonNull String scheduledTime,
int status, long snoozeDurationMillis) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mScheduledTime = Preconditions.checkNotNull(scheduledTime);
mStatus = status;
mSnoozeDurationMillis = snoozeDurationMillis;
@@ -197,6 +198,7 @@
public AlarmInstance build() {
return new AlarmInstance(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions,
mScheduledTime, mStatus, mSnoozeDurationMillis);
}
}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ContactPoint.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ContactPoint.java
index 84333a6..739e5a6 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ContactPoint.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ContactPoint.java
@@ -57,12 +57,13 @@
@Nullable String description,
@Nullable String image,
@Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
@NonNull String label,
@NonNull List<String> addresses,
@NonNull List<String> emails,
@NonNull List<String> telephones) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mLabel = label;
mAddresses = Collections.unmodifiableList(addresses);
mEmails = Collections.unmodifiableList(emails);
@@ -181,6 +182,7 @@
/*description=*/ mDescription,
/*image=*/ mImage,
/*url=*/ mUrl,
+ /*potentialActions=*/ mPotentialActions,
/*label=*/ mLabel,
/*addresses=*/ new ArrayList<>(mAddresses),
/*emails=*/ new ArrayList<>(mEmails),
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ImageObject.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ImageObject.java
index 2586d9c..b756437 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ImageObject.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/ImageObject.java
@@ -51,10 +51,12 @@
ImageObject(@NonNull String namespace, @NonNull String id, int documentScore,
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
- @Nullable String image, @Nullable String url, @NonNull List<Keyword> keywords,
+ @Nullable String image, @Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
+ @NonNull List<Keyword> keywords,
@Nullable String sha256, @Nullable String thumbnailSha256) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mKeywords = checkNotNull(keywords);
mSha256 = sha256;
mThumbnailSha256 = thumbnailSha256;
@@ -157,7 +159,7 @@
public ImageObject build() {
return new ImageObject(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
- new ArrayList<>(mKeywords), mSha256, mThumbnailSha256);
+ mPotentialActions, new ArrayList<>(mKeywords), mSha256, mThumbnailSha256);
}
/**
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Person.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Person.java
index bbd50e29..062f799 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Person.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Person.java
@@ -158,6 +158,7 @@
@Nullable String description,
@Nullable String image,
@Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
@Nullable String givenName,
@Nullable String middleName,
@Nullable String familyName,
@@ -172,7 +173,7 @@
@NonNull List<String> relations,
@NonNull List<ContactPoint> contactPoints) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mGivenName = givenName;
mMiddleName = middleName;
mFamilyName = familyName;
@@ -524,6 +525,7 @@
/*description=*/ mDescription,
/*image=*/ mImage,
/*url=*/ mUrl,
+ /*potentialActions=*/ mPotentialActions,
/*givenName=*/ mGivenName,
/*middleName=*/ mMiddleName,
/*familyName=*/ mFamilyName,
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/PotentialAction.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/PotentialAction.java
new file mode 100644
index 0000000..7d4e7e4
--- /dev/null
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/PotentialAction.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.builtintypes;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appsearch.annotation.Document;
+import androidx.core.util.Preconditions;
+
+/**
+ * An AppSearch document representing an action. This action schema type is used for the nested
+ * potentialActions field in entity schema types such as builtin:Thing or builtin:Timer.
+ *
+ * <ul>
+ * <li><b>name</b> - This is a unique identifier for the action, such as
+ * "actions.intent.CREATE_CALL". See <a
+ * href=developer.android.com/reference/app-actions/built-in-intents/communications/create-call>
+ * developer.android.com/reference/app-actions/built-in-intents/communications/create-call</a>
+ * for an sample Action type.
+ * </li>
+ * <li><b>description</b> - A brief description of what the action does, such as "Create call" or
+ * "Create Message".</li>
+ * <li><b>uri</b> - A deeplink URI linking to an action. Invoking the action can be done by
+ * creating an {@link android.content.Intent} object by calling
+ * {@link android.content.Intent#parseUri} with the deeplink URI. Creating a deeplink URI, and
+ * adding intent extras, can be done by building an intent and calling
+ * {@link android.content.Intent#toUri}.
+ * </li>
+ * </ul>
+ */
+// TODO(b/274671459): Add additional information, if needed, to dispatch actions.
+@Document(name = "builtin:PotentialAction")
+public class PotentialAction {
+ @Document.Namespace
+ final String mNamespace;
+
+ @Document.Id
+ final String mId;
+
+ @Document.StringProperty
+ private final String mName;
+
+ @Document.StringProperty
+ private final String mDescription;
+
+ @Document.StringProperty
+ private final String mUri;
+
+ PotentialAction(@NonNull String namespace, @NonNull String id, @Nullable String name,
+ @Nullable String description, @Nullable String uri) {
+ mNamespace = Preconditions.checkNotNull(namespace);
+ mId = Preconditions.checkNotNull(id);
+ mName = name;
+ mDescription = description;
+ mUri = uri;
+ }
+
+ /** Returns a string describing the action. */
+ @Nullable
+ public String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Returns the BII action ID, which comes from Action IDs of Built-in intents listed at <a
+ * href=developer.android.com/reference/app-actions/built-in-intents/bii-index>
+ * developer.android.com/reference/app-actions/built-in-intents/bii-index</a>. For example,
+ * the "Start Exercise" BII has an action id of "actions.intent.START_EXERCISE".
+ */
+ @Nullable
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the deeplink URI.
+ *
+ * <p> A deeplink URI is a URI that lets a user access a specific content or feature within an
+ * app directly. Users can create one by adding parameters to the app's base URI. To use a
+ * deeplink URI in an Android application, users can create an {@link android.content.Intent}
+ * object by calling {@link android.content.Intent#parseUri} with the deeplink URI. Creating a
+ * deeplink URI, and adding intent extras, can be done by building an intent and calling
+ * {@link android.content.Intent#toUri}.
+ */
+ @Nullable
+ public String getUri() {
+ return mUri;
+ }
+
+ /** Builder for {@link PotentialAction}. */
+ public static final class Builder {
+ @Nullable private String mName;
+ @Nullable private String mDescription;
+ @Nullable private String mUri;
+
+ /**
+ * Constructor for {@link PotentialAction.Builder}.
+ *
+ * <p> As PotentialAction is used as a DocumentProperty of Thing, it does not need an id or
+ * namespace.
+ */
+ public Builder() { }
+
+ /**
+ * Constructor with all the existing values.
+ *
+ * <p> As PotentialAction is used as a DocumentProperty of Thing, it does not need an id or
+ * namespace.
+ */
+ public Builder(@NonNull PotentialAction potentialAction) {
+ mName = potentialAction.getName();
+ mDescription = potentialAction.getDescription();
+ mUri = potentialAction.getUri();
+ }
+
+ /** Sets the name of the action. */
+ @NonNull
+ public Builder setName(@Nullable String name) {
+ mName = name;
+ return this;
+ }
+
+ /** Sets the description of the action, such as "Call". */
+ @NonNull
+ public Builder setDescription(@Nullable String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets the deeplink URI of the Action.
+ *
+ * <p> A deeplink URI is a URI that lets a user access a specific content or feature within
+ * an app directly. Users can create one by adding parameters to the app's base URI. To use
+ * a deeplink URI in an Android application, users can create an
+ * {@link android.content.Intent} object by calling
+ * {@link android.content.Intent#parseUri} with the deeplink URI. Creating a deeplink URI,
+ * and adding intent extras, can be done by building an intent and calling
+ * {@link android.content.Intent#toUri}.
+ */
+ @NonNull
+ public Builder setUri(@Nullable String uri) {
+ mUri = uri;
+ return this;
+ }
+
+ /** Builds the {@link PotentialAction}. */
+ @NonNull
+ public PotentialAction build() {
+ // As PotentialAction is used as a DocumentProperty of Thing, it does not need an id or
+ // namespace.
+ return new PotentialAction(/*namespace=*/"", /*id=*/"", mName, mDescription, mUri);
+ }
+ }
+}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Stopwatch.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Stopwatch.java
index a3fc290..6c049b8 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Stopwatch.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Stopwatch.java
@@ -77,11 +77,12 @@
Stopwatch(@NonNull String namespace, @NonNull String id, int documentScore,
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
- @Nullable String image, @Nullable String url, long baseTimeMillis,
- long baseTimeMillisInElapsedRealtime, int bootCount, int status,
+ @Nullable String image, @Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
+ long baseTimeMillis, long baseTimeMillisInElapsedRealtime, int bootCount, int status,
long accumulatedDurationMillis, @NonNull List<StopwatchLap> laps) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mBaseTimeMillis = baseTimeMillis;
mBaseTimeMillisInElapsedRealtime = baseTimeMillisInElapsedRealtime;
mBootCount = bootCount;
@@ -318,6 +319,7 @@
public Stopwatch build() {
return new Stopwatch(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions,
mBaseTimeMillis, mBaseTimeMillisInElapsedRealtime, mBootCount, mStatus,
mAccumulatedDurationMillis, mLaps);
}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/StopwatchLap.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/StopwatchLap.java
index 1923629..f5b979a 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/StopwatchLap.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/StopwatchLap.java
@@ -45,10 +45,11 @@
StopwatchLap(@NonNull String namespace, @NonNull String id, int documentScore,
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
- @Nullable String image, @Nullable String url, int lapNumber,
- long lapDurationMillis, long accumulatedLapDurationMillis) {
+ @Nullable String image, @Nullable String url,
+ @NonNull List<PotentialAction> potentialActions,
+ int lapNumber, long lapDurationMillis, long accumulatedLapDurationMillis) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mLapNumber = lapNumber;
mLapDurationMillis = lapDurationMillis;
mAccumulatedLapDurationMillis = accumulatedLapDurationMillis;
@@ -146,6 +147,7 @@
public StopwatchLap build() {
return new StopwatchLap(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions,
mLapNumber, mLapDurationMillis, mAccumulatedLapDurationMillis);
}
}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Thing.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Thing.java
index 58629bc..954e949 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Thing.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Thing.java
@@ -65,10 +65,14 @@
@Document.StringProperty
private final String mUrl;
+ @Document.DocumentProperty
+ private final List<PotentialAction> mPotentialActions;
+
Thing(@NonNull String namespace, @NonNull String id, int documentScore,
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
- @Nullable String image, @Nullable String url) {
+ @Nullable String image, @Nullable String url,
+ @Nullable List<PotentialAction> potentialActions) {
mNamespace = Preconditions.checkNotNull(namespace);
mId = Preconditions.checkNotNull(id);
mDocumentScore = documentScore;
@@ -85,6 +89,13 @@
mDescription = description;
mImage = image;
mUrl = url;
+ // AppSearch may pass null if old schema lacks the potentialActions field during
+ // GenericDocument to Java class conversion.
+ if (potentialActions == null) {
+ mPotentialActions = Collections.emptyList();
+ } else {
+ mPotentialActions = Collections.unmodifiableList(potentialActions);
+ }
}
/** Returns the namespace (or logical grouping) for this item. */
@@ -154,6 +165,12 @@
return mUrl;
}
+ /** Returns the actions that can be taken on this object. */
+ @NonNull
+ public List<PotentialAction> getPotentialActions() {
+ return mPotentialActions;
+ }
+
/** Builder for {@link Thing}. */
public static final class Builder extends BuilderImpl<Builder> {
/** Constructs {@link Thing.Builder} with given {@code namespace} and {@code id} */
@@ -181,6 +198,8 @@
protected String mDescription;
protected String mImage;
protected String mUrl;
+ protected List<PotentialAction> mPotentialActions = new ArrayList<>();
+ private boolean mBuilt = false;
BuilderImpl(@NonNull String namespace, @NonNull String id) {
mNamespace = Preconditions.checkNotNull(namespace);
@@ -201,6 +220,7 @@
mDescription = thing.getDescription();
mImage = thing.getImage();
mUrl = thing.getUrl();
+ mPotentialActions = new ArrayList<>(thing.getPotentialActions());
}
/**
@@ -214,6 +234,7 @@
@NonNull
@SuppressWarnings("unchecked")
public T setDocumentScore(int documentScore) {
+ resetIfBuilt();
mDocumentScore = documentScore;
return (T) this;
}
@@ -233,6 +254,7 @@
@NonNull
@SuppressWarnings("unchecked")
public T setCreationTimestampMillis(long creationTimestampMillis) {
+ resetIfBuilt();
mCreationTimestampMillis = creationTimestampMillis;
return (T) this;
}
@@ -251,6 +273,7 @@
@NonNull
@SuppressWarnings("unchecked")
public T setDocumentTtlMillis(long documentTtlMillis) {
+ resetIfBuilt();
mDocumentTtlMillis = documentTtlMillis;
return (T) this;
}
@@ -258,6 +281,7 @@
/** Sets the name of the item. */
@NonNull
public T setName(@Nullable String name) {
+ resetIfBuilt();
mName = name;
return (T) this;
}
@@ -265,6 +289,7 @@
/** Adds an alias for the item. */
@NonNull
public T addAlternateName(@NonNull String alternateName) {
+ resetIfBuilt();
Preconditions.checkNotNull(alternateName);
mAlternateNames.add(alternateName);
return (T) this;
@@ -273,6 +298,7 @@
/** Clears the aliases, if any, for the item. */
@NonNull
public T clearAlternateNames() {
+ resetIfBuilt();
mAlternateNames.clear();
return (T) this;
}
@@ -280,6 +306,7 @@
/** Sets the description for the item. */
@NonNull
public T setDescription(@Nullable String description) {
+ resetIfBuilt();
mDescription = description;
return (T) this;
}
@@ -287,6 +314,7 @@
/** Sets the URL for an image of the item. */
@NonNull
public T setImage(@Nullable String image) {
+ resetIfBuilt();
mImage = image;
return (T) this;
}
@@ -307,15 +335,46 @@
*/
@NonNull
public T setUrl(@Nullable String url) {
+ resetIfBuilt();
mUrl = url;
return (T) this;
}
+ /**
+ * Add a new action to the list of potential actions for this document.
+ */
+ @NonNull
+ public T addPotentialAction(@NonNull PotentialAction newPotentialAction) {
+ resetIfBuilt();
+ Preconditions.checkNotNull(newPotentialAction);
+ mPotentialActions.add(newPotentialAction);
+ return (T) this;
+ }
+
+ /**
+ * Clear all the potential actions for this document.
+ */
+ @NonNull
+ public T clearPotentialActions() {
+ resetIfBuilt();
+ mPotentialActions.clear();
+ return (T) this;
+ }
+
+ private void resetIfBuilt() {
+ if (mBuilt) {
+ mAlternateNames = new ArrayList<>(mAlternateNames);
+ mPotentialActions = new ArrayList<>(mPotentialActions);
+ mBuilt = false;
+ }
+ }
/** Builds a {@link Thing} object. */
@NonNull
public Thing build() {
+ mBuilt = true;
return new Thing(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
- mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl);
+ mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions);
}
}
}
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Timer.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Timer.java
index 11d8961..ef842e4 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Timer.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Timer.java
@@ -91,12 +91,13 @@
long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
@Nullable List<String> alternateNames, @Nullable String description,
@Nullable String image, @Nullable String url,
+ @Nullable List<PotentialAction> potentialActions,
long durationMillis, long originalDurationMillis, long startTimeMillis,
long baseTimeMillis, long baseTimeMillisInElapsedRealtime, int bootCount,
long remainingDurationMillis, @Nullable String ringtone, int status,
boolean shouldVibrate) {
super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
- alternateNames, description, image, url);
+ alternateNames, description, image, url, potentialActions);
mDurationMillis = durationMillis;
mOriginalDurationMillis = originalDurationMillis;
mStartTimeMillis = startTimeMillis;
@@ -477,6 +478,7 @@
public Timer build() {
return new Timer(mNamespace, mId, mDocumentScore, mCreationTimestampMillis,
mDocumentTtlMillis, mName, mAlternateNames, mDescription, mImage, mUrl,
+ mPotentialActions,
mDurationMillis, mOriginalDurationMillis, mStartTimeMillis, mBaseTimeMillis,
mBaseTimeMillisInElapsedRealtime, mBootCount, mRemainingDurationMillis,
mRingtone, mStatus, mShouldVibrate);
diff --git a/appsearch/appsearch-local-storage/proguard-rules.pro b/appsearch/appsearch-local-storage/proguard-rules.pro
index 82c4b719..335e9e8 100644
--- a/appsearch/appsearch-local-storage/proguard-rules.pro
+++ b/appsearch/appsearch-local-storage/proguard-rules.pro
@@ -19,7 +19,7 @@
<fields>;
}
-keep class com.google.android.icing.BreakIteratorBatcher { *; }
--keepclassmembers public class com.google.android.icing.IcingSearchEngine {
+-keepclassmembers public class com.google.android.icing.IcingSearchEngineImpl {
private long nativePointer;
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 63bba79..daf7d3a 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -113,6 +113,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -493,8 +494,8 @@
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
mAppSearchImpl.close();
mAppSearchImpl = AppSearchImpl.create(
- mAppSearchDir, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE,
- /*visibilityChecker=*/null);
+ mAppSearchDir, new UnlimitedLimitConfig(), new DefaultIcingOptionsConfig(),
+ initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
// Check recovery state
InitializeStats initStats = initStatsBuilder.build();
@@ -2491,6 +2492,7 @@
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2560,6 +2562,7 @@
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2636,6 +2639,7 @@
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2763,6 +2767,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2843,6 +2848,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2901,6 +2907,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -2939,6 +2946,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3053,6 +3061,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3150,6 +3159,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3208,6 +3218,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3328,7 +3339,10 @@
@Test
public void testRemoveByQuery_withJoinSpec_throwsException() {
Exception e = assertThrows(IllegalArgumentException.class,
- () -> mAppSearchImpl.removeByQuery("", "", "",
+ () -> mAppSearchImpl.removeByQuery(
+ /*packageName=*/"",
+ /*databaseName=*/"",
+ /*queryExpression=*/"",
new SearchSpec.Builder()
.setJoinSpec(new JoinSpec.Builder("childProp").build())
.build(),
@@ -3359,6 +3373,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3443,6 +3458,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3501,6 +3517,7 @@
return Integer.MAX_VALUE;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3548,6 +3565,7 @@
return 2;
}
},
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -3649,6 +3667,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -3699,6 +3718,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -3747,6 +3767,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -3797,6 +3818,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -4139,6 +4161,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -4177,6 +4200,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -4207,6 +4231,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -4302,6 +4327,7 @@
mAppSearchImpl = AppSearchImpl.create(
tempFolder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
mockVisibilityChecker);
@@ -4388,6 +4414,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
rejectChecker);
@@ -4489,6 +4516,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
visibilityChecker);
@@ -4548,6 +4576,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
rejectChecker);
@@ -4873,6 +4902,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
visibilityChecker);
@@ -5028,6 +5058,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
visibilityChecker);
@@ -5114,6 +5145,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
visibilityChecker);
@@ -5204,6 +5236,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/null,
ALWAYS_OPTIMIZE,
visibilityChecker);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
index bca29e4..c9828f5 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
@@ -22,6 +22,7 @@
import androidx.appsearch.app.AppSearchSchema;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.JoinSpec;
import androidx.appsearch.app.SearchResultPage;
import androidx.appsearch.app.SearchSpec;
import androidx.appsearch.exceptions.AppSearchException;
@@ -54,6 +55,7 @@
import org.junit.rules.TemporaryFolder;
import java.io.File;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -74,6 +76,7 @@
mAppSearchImpl = AppSearchImpl.create(
mTemporaryFolder.newFolder(),
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -348,6 +351,7 @@
AppSearchImpl appSearchImpl = AppSearchImpl.create(
mTemporaryFolder.newFolder(),
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
initStatsBuilder,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -378,6 +382,7 @@
AppSearchImpl appSearchImpl = AppSearchImpl.create(
folder,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
@@ -414,8 +419,8 @@
// Create another appsearchImpl on the same folder
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl = AppSearchImpl.create(
- folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE,
- /*visibilityChecker=*/null);
+ folder, new UnlimitedLimitConfig(), new DefaultIcingOptionsConfig(),
+ initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
InitializeStats iStats = initStatsBuilder.build();
assertThat(iStats).isNotNull();
@@ -441,8 +446,8 @@
final File folder = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl = AppSearchImpl.create(
- folder, new UnlimitedLimitConfig(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
- /*visibilityChecker=*/null);
+ folder, new UnlimitedLimitConfig(), new DefaultIcingOptionsConfig(),
+ /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
List<AppSearchSchema> schemas = ImmutableList.of(
new AppSearchSchema.Builder("Type1").build(),
@@ -480,8 +485,8 @@
// Create another appsearchImpl on the same folder
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl = AppSearchImpl.create(
- folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE,
- /*visibilityChecker=*/null);
+ folder, new UnlimitedLimitConfig(), new DefaultIcingOptionsConfig(),
+ initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
InitializeStats iStats = initStatsBuilder.build();
// Some of other fields are already covered by AppSearchImplTest#testReset()
@@ -708,6 +713,160 @@
}
@Test
+ public void testLoggingStats_search_join() throws Exception {
+ AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("entityId")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build()
+ ).build();
+
+ AppSearchSchema entitySchema = new AppSearchSchema.Builder("entity")
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ List<AppSearchSchema> schemas = Arrays.asList(actionSchema, entitySchema);
+
+ // Insert schema
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityDocuments=*/ Collections.emptyList(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
+
+ assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+ GenericDocument entity1 =
+ new GenericDocument.Builder<>("namespace", "id1", "entity")
+ .setPropertyString("subject", "an entity")
+ .build();
+ GenericDocument entity2 =
+ new GenericDocument.Builder<>("namespace", "id2", "entity")
+ .setPropertyString("subject", "another entity")
+ .build();
+
+ GenericDocument action1 =
+ new GenericDocument.Builder<>("namespace", "action1", "ViewAction")
+ .setPropertyString("entityId",
+ "testPackage$testDatabase/namespace#id1")
+ .build();
+ GenericDocument action2 =
+ new GenericDocument.Builder<>("namespace", "action2", "ViewAction")
+ .setPropertyString("entityId",
+ "testPackage$testDatabase/namespace#id1")
+ .build();
+ GenericDocument action3 =
+ new GenericDocument.Builder<>("namespace", "action3", "ViewAction")
+ .setPropertyString("entityId",
+ "testPackage$testDatabase/namespace#id1")
+ .build();
+ GenericDocument action4 =
+ new GenericDocument.Builder<>("namespace", "action4", "ViewAction")
+ .setPropertyString("entityId",
+ "testPackage$testDatabase/namespace#id2")
+ .build();
+
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ entity1,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ entity2,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ action1,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ action2,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ action3,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+ mAppSearchImpl.putDocument(
+ testPackageName,
+ testDatabase,
+ action4,
+ /*sendChangeNotifications=*/ false,
+ mLogger);
+
+ SearchSpec nestedSearchSpec =
+ new SearchSpec.Builder()
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+ .setOrder(SearchSpec.ORDER_ASCENDING)
+ .build();
+
+ JoinSpec js = new JoinSpec.Builder("entityId")
+ .setNestedSearch("", nestedSearchSpec)
+ .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+ .build();
+
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+ .setJoinSpec(js)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+
+ String queryStr = "entity";
+ SearchResultPage searchResultPage = mAppSearchImpl.query(testPackageName, testDatabase,
+ queryStr, searchSpec, /*logger=*/ mLogger);
+
+ assertThat(searchResultPage.getResults()).hasSize(2);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(entity1);
+ assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(entity2);
+
+ SearchStats sStats = mLogger.mSearchStats;
+
+ assertThat(sStats).isNotNull();
+
+ assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
+ assertThat(sStats.getDatabase()).isEqualTo(testDatabase);
+ assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL);
+ assertThat(sStats.getTermCount()).isEqualTo(1);
+ assertThat(sStats.getQueryLength()).isEqualTo(queryStr.length());
+ assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
+ assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(2);
+ assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(2);
+ assertThat(sStats.isFirstPage()).isTrue();
+ assertThat(sStats.getRankingStrategy()).isEqualTo(
+ ScoringSpecProto.RankingStrategy.Code.JOIN_AGGREGATE_SCORE_VALUE);
+ assertThat(sStats.getScoredDocumentCount()).isEqualTo(2);
+ assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(0);
+ // Join-specific stats. If the process goes really fast, the total latency could be 0.
+ // Since the default of total latency is also 0, we just remove the assertion on
+ // JoinLatencyMillis.
+ assertThat(sStats.getJoinType()).isEqualTo(
+ AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+ assertThat(sStats.getNumJoinedResultsCurrentPage()).isEqualTo(4);
+ }
+
+ @Test
public void testLoggingStats_remove_success() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
index dff83a2..6840a98 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
@@ -52,6 +52,7 @@
mAppSearchImpl = AppSearchImpl.create(
mTemporaryFolder.newFolder(),
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
index 3f2cb70..95cf9c5 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
@@ -20,6 +20,7 @@
import androidx.appsearch.app.AppSearchSchema;
+import com.google.android.icing.proto.JoinableConfig;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.android.icing.proto.StringIndexingConfig;
@@ -118,4 +119,41 @@
assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto))
.isEqualTo(musicRecordingSchema);
}
+
+ @Test
+ public void testGetProto_JoinableConfig() {
+ AppSearchSchema albumSchema = new AppSearchSchema.Builder("Album")
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("artist")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setDeletionPropagation(true)
+ .build()
+ ).build();
+
+ JoinableConfig joinableConfig = JoinableConfig.newBuilder()
+ .setValueType(JoinableConfig.ValueType.Code.QUALIFIED_ID)
+ .setPropagateDelete(true)
+ .build();
+
+ SchemaTypeConfigProto expectedAlbumProto = SchemaTypeConfigProto.newBuilder()
+ .setSchemaType("Album")
+ .setVersion(0)
+ .addProperties(
+ PropertyConfigProto.newBuilder()
+ .setPropertyName("artist")
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setStringIndexingConfig(StringIndexingConfig.newBuilder()
+ .setTermMatchType(TermMatchType.Code.UNKNOWN)
+ .setTokenizerType(
+ StringIndexingConfig.TokenizerType.Code.NONE))
+ .setJoinableConfig(joinableConfig))
+ .build();
+
+ assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(albumSchema, /*version=*/0))
+ .isEqualTo(expectedAlbumProto);
+ assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedAlbumProto))
+ .isEqualTo(albumSchema);
+ }
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
index 271bf2a..b47bd0a 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
@@ -26,6 +26,8 @@
import androidx.appsearch.app.JoinSpec;
import androidx.appsearch.app.SearchSpec;
import androidx.appsearch.localstorage.AppSearchImpl;
+import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
+import androidx.appsearch.localstorage.IcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -43,6 +45,8 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -58,6 +62,26 @@
@Rule
public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+ private final IcingOptionsConfig mDefaultIcingOptionsConfig = new DefaultIcingOptionsConfig();
+
+ private AppSearchImpl mAppSearchImpl;
+
+ @Before
+ public void setUp() throws Exception {
+ mAppSearchImpl = AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ mDefaultIcingOptionsConfig,
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE,
+ /*visibilityChecker=*/null);
+ }
+
+ @After
+ public void tearDown() {
+ mAppSearchImpl.close();
+ }
+
@Test
public void testToSearchSpecProto() throws Exception {
SearchSpec searchSpec = new SearchSpec.Builder().build();
@@ -70,19 +94,20 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
/*namespaceMap=*/ImmutableMap.of(
- prefix1, ImmutableSet.of(
- prefix1 + "namespace1",
- prefix1 + "namespace2"),
- prefix2, ImmutableSet.of(
- prefix2 + "namespace1",
- prefix2 + "namespace2")),
+ prefix1, ImmutableSet.of(
+ prefix1 + "namespace1",
+ prefix1 + "namespace2"),
+ prefix2, ImmutableSet.of(
+ prefix2 + "namespace1",
+ prefix2 + "namespace2")),
/*schemaMap=*/ImmutableMap.of(
- prefix1, ImmutableMap.of(
- prefix1 + "typeA", configProto,
- prefix1 + "typeB", configProto),
- prefix2, ImmutableMap.of(
- prefix2 + "typeA", configProto,
- prefix2 + "typeB", configProto)));
+ prefix1, ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2, ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto)),
+ mDefaultIcingOptionsConfig);
// Convert SearchSpec to proto.
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -93,6 +118,7 @@
assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
"package$database1/namespace1", "package$database1/namespace2",
"package$database2/namespace1", "package$database2/namespace2");
+ assertThat(searchSpecProto.getUseReadOnlySearch()).isTrue();
}
@Test
@@ -106,7 +132,6 @@
JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
.setNestedSearch("nestedQuery", nestedSearchSpec)
.setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_SUM_RANKING_SIGNAL)
- .setMaxJoinedResultCount(10)
.build();
searchSpec.setJoinSpec(joinSpec);
@@ -131,7 +156,8 @@
prefix1 + "typeB", configProto),
prefix2, ImmutableMap.of(
prefix2 + "typeA", configProto,
- prefix2 + "typeB", configProto)));
+ prefix2 + "typeB", configProto)),
+ mDefaultIcingOptionsConfig);
// Convert SearchSpec to proto.
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -149,11 +175,10 @@
JoinSpecProto joinSpecProto = searchSpecProto.getJoinSpec();
assertThat(joinSpecProto.hasNestedSpec()).isTrue();
- assertThat(joinSpecProto.getParentPropertyExpression()).isEqualTo(JoinSpec.QUALIFIED_ID);
+ assertThat(joinSpecProto.getParentPropertyExpression()).isEqualTo("this.qualifiedId()");
assertThat(joinSpecProto.getChildPropertyExpression()).isEqualTo("childPropertyExpression");
assertThat(joinSpecProto.getAggregationScoringStrategy())
.isEqualTo(JoinSpecProto.AggregationScoringStrategy.Code.SUM);
- assertThat(joinSpecProto.getMaxJoinedChildCount()).isEqualTo(10);
JoinSpecProto.NestedSpecProto nestedSpecProto = joinSpecProto.getNestedSpec();
assertThat(nestedSpecProto.getSearchSpec().getQuery()).isEqualTo("nestedQuery");
@@ -161,6 +186,87 @@
ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP);
}
+ @Test
+ public void testToSearchSpec_withJoinSpec_childSearchesOtherSchema() throws Exception {
+ String prefix1 = PrefixUtil.createPrefix("package", "database1");
+ String prefix2 = PrefixUtil.createPrefix("package", "database2");
+
+ SearchSpec nestedSearchSpec =
+ new SearchSpec.Builder()
+ .addFilterPackageNames("package")
+ .addFilterSchemas("typeA")
+ .build();
+ SearchSpec.Builder searchSpec =
+ new SearchSpec.Builder()
+ .addFilterPackageNames("package")
+ .addFilterSchemas("typeB");
+
+ // Create a JoinSpec object and set it in the converter
+ JoinSpec joinSpec =
+ new JoinSpec.Builder("childPropertyExpression")
+ .setNestedSearch("nestedQuery", nestedSearchSpec)
+ .build();
+
+ searchSpec.setJoinSpec(joinSpec);
+
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ SearchSpecToProtoConverter converter =
+ new SearchSpecToProtoConverter(
+ /*queryExpression=*/ "query",
+ searchSpec.build(),
+ /*prefixes=*/ ImmutableSet.of(prefix1, prefix2),
+ /*namespaceMap=*/ ImmutableMap.of(
+ prefix1,
+ ImmutableSet.of(
+ prefix1 + "namespace1", prefix1 + "namespace2"),
+ prefix2,
+ ImmutableSet.of(
+ prefix2 + "namespace1", prefix2 + "namespace2")),
+ /*schemaMap=*/ ImmutableMap.of(
+ prefix1,
+ ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2,
+ ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto)),
+ mDefaultIcingOptionsConfig);
+
+ VisibilityStore visibilityStore = new VisibilityStore(mAppSearchImpl);
+ converter.removeInaccessibleSchemaFilter(
+ new CallerAccess(/*callingPackageName=*/"package"),
+ visibilityStore,
+ AppSearchTestUtils.createMockVisibilityChecker(
+ /*visiblePrefixedSchemas=*/ ImmutableSet.of(
+ prefix1 + "typeA", prefix1 + "typeB", prefix2 + "typeA",
+ prefix2 + "typeB")));
+
+ // Convert SearchSpec to proto.
+ SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
+
+ assertThat(searchSpecProto.getQuery()).isEqualTo("query");
+ assertThat(searchSpecProto.getSchemaTypeFiltersList())
+ .containsExactly(
+ "package$database1/typeB",
+ "package$database2/typeB");
+ assertThat(searchSpecProto.getNamespaceFiltersList())
+ .containsExactly(
+ "package$database1/namespace1", "package$database1/namespace2",
+ "package$database2/namespace1", "package$database2/namespace2");
+
+ // Assert that the joinSpecProto is set correctly in the searchSpecProto
+ assertThat(searchSpecProto.hasJoinSpec()).isTrue();
+
+ JoinSpecProto joinSpecProto = searchSpecProto.getJoinSpec();
+ assertThat(joinSpecProto.hasNestedSpec()).isTrue();
+
+ JoinSpecProto.NestedSpecProto nestedSpecProto = joinSpecProto.getNestedSpec();
+ assertThat(nestedSpecProto.getSearchSpec().getSchemaTypeFiltersList())
+ .containsExactly(
+ "package$database1/typeA",
+ "package$database2/typeA");
+ }
@Test
public void testToScoringSpecProto() {
@@ -177,7 +283,8 @@
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/ImmutableMap.of(prefix, ImmutableSet.of(prefix + namespace)),
/*schemaMap=*/ImmutableMap.of(prefix, ImmutableMap.of(prefix + schemaType,
- SchemaTypeConfigProto.getDefaultInstance()))).toScoringSpecProto();
+ SchemaTypeConfigProto.getDefaultInstance())),
+ mDefaultIcingOptionsConfig).toScoringSpecProto();
TypePropertyWeights typePropertyWeights = TypePropertyWeights.newBuilder()
.setSchemaType(prefix + schemaType)
.addPropertyWeights(PropertyWeight.newBuilder()
@@ -205,7 +312,8 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(),
/*namespaceMap=*/ImmutableMap.of(),
- /*schemaMap=*/ImmutableMap.of()).toScoringSpecProto();
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig).toScoringSpecProto();
assertThat(scoringSpecProto.getOrderBy().getNumber())
.isEqualTo(ScoringSpecProto.Order.Code.ASC_VALUE);
@@ -229,9 +337,11 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(),
/*namespaceMap=*/ImmutableMap.of(),
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
ResultSpecProto resultSpecProto = convert.toResultSpecProto(
- /*namespaceMap=*/ImmutableMap.of());
+ /*namespaceMap=*/ImmutableMap.of(),
+ /*schemaMap=*/ImmutableMap.of());
assertThat(resultSpecProto.getNumPerPage()).isEqualTo(123);
assertThat(resultSpecProto.getSnippetSpec().getNumToSnippet()).isEqualTo(234);
@@ -240,6 +350,47 @@
}
@Test
+ public void testToResultSpecProtoWithJoinSpec() throws Exception {
+ SearchSpec nestedSearchSpec = new SearchSpec.Builder()
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP).build();
+
+ // Create a JoinSpec object and set it in the converter
+ JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
+ .setNestedSearch("nestedQuery", nestedSearchSpec)
+ .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_SUM_RANKING_SIGNAL)
+ .setMaxJoinedResultCount(10)
+ .build();
+
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+ .setJoinSpec(joinSpec)
+ .setResultCountPerPage(123)
+ .setSnippetCount(234)
+ .setSnippetCountPerProperty(345)
+ .setMaxSnippetSize(456)
+ .build();
+
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"query",
+ searchSpec,
+ /*prefixes=*/ImmutableSet.of(),
+ /*namespaceMap=*/ImmutableMap.of(),
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
+
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+ /*namespaceMap=*/ImmutableMap.of(),
+ /*schemaMap=*/ImmutableMap.of());
+
+ assertThat(resultSpecProto.getNumPerPage()).isEqualTo(123);
+ assertThat(resultSpecProto.getSnippetSpec().getNumToSnippet()).isEqualTo(234);
+ assertThat(resultSpecProto.getSnippetSpec().getNumMatchesPerProperty()).isEqualTo(345);
+ assertThat(resultSpecProto.getSnippetSpec().getMaxWindowUtf32Length()).isEqualTo(456);
+ assertThat(resultSpecProto.getMaxJoinedChildrenPerParentToReturn()).isEqualTo(10);
+ }
+
+ @Test
public void testToResultSpecProto_groupByPackage() {
SearchSpec searchSpec = new SearchSpec.Builder()
.setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 5)
@@ -253,7 +404,8 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
/*namespaceMap=*/ImmutableMap.of(),
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
ResultSpecProto resultSpecProto = converter.toResultSpecProto(
/*namespaceMap=*/ImmutableMap.of(
prefix1, ImmutableSet.of(
@@ -261,7 +413,8 @@
prefix1 + "namespaceB"),
prefix2, ImmutableSet.of(
prefix2 + "namespaceA",
- prefix2 + "namespaceB")));
+ prefix2 + "namespaceB")),
+ /*schemaMap=*/ImmutableMap.of());
assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
// First grouping should have same package name.
@@ -304,8 +457,11 @@
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+ namespaceMap,
/*schemaMap=*/ImmutableMap.of());
- ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap);
assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
// First grouping should have same namespace.
@@ -326,10 +482,57 @@
}
@Test
+ public void testToResultSpecProto_groupBySchema() throws Exception {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_SCHEMA, 5)
+ .build();
+
+ String prefix1 = PrefixUtil.createPrefix("package1", "database");
+ String prefix2 = PrefixUtil.createPrefix("package2", "database");
+
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+ prefix1, ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2, ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto));
+
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"query",
+ searchSpec,
+ /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
+ /*namespaceMap=*/ImmutableMap.of(),
+ schemaMap,
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+ /*namespaceMap=*/ImmutableMap.of(),
+ schemaMap);
+
+ assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
+ // First grouping should have the same schema type.
+ ResultSpecProto.ResultGrouping grouping1 = resultSpecProto.getResultGroupings(0);
+ assertThat(grouping1.getEntryGroupingsList()).hasSize(2);
+ assertThat(
+ PrefixUtil.removePrefix(grouping1.getEntryGroupings(0).getSchema()))
+ .isEqualTo(
+ PrefixUtil.removePrefix(grouping1.getEntryGroupings(1).getSchema()));
+
+ // Second grouping should have the same schema type.
+ ResultSpecProto.ResultGrouping grouping2 = resultSpecProto.getResultGroupings(1);
+ assertThat(grouping2.getEntryGroupingsList()).hasSize(2);
+ assertThat(
+ PrefixUtil.removePrefix(grouping2.getEntryGroupings(0).getSchema()))
+ .isEqualTo(
+ PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getSchema()));
+ }
+
+ @Test
public void testToResultSpecProto_groupByNamespaceAndPackage() throws Exception {
SearchSpec searchSpec = new SearchSpec.Builder()
.setResultGrouping(GROUPING_TYPE_PER_PACKAGE
- | SearchSpec.GROUPING_TYPE_PER_NAMESPACE, 5)
+ | SearchSpec.GROUPING_TYPE_PER_NAMESPACE, 5)
.build();
String prefix1 = PrefixUtil.createPrefix("package1", "database");
@@ -346,8 +549,11 @@
/*queryExpression=*/"query",
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
- namespaceMap, /*schemaMap=*/ImmutableMap.of());
- ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap);
+ namespaceMap, /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+ namespaceMap,
+ /*schemaMap=*/ImmutableMap.of());
// All namespace should be separated.
assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
@@ -358,6 +564,244 @@
}
@Test
+ public void testToResultSpecProto_groupBySchemaAndPackage() throws Exception {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setResultGrouping(GROUPING_TYPE_PER_PACKAGE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, 5)
+ .build();
+
+ String prefix1 = PrefixUtil.createPrefix("package1", "database");
+ String prefix2 = PrefixUtil.createPrefix("package2", "database");
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+ prefix1, ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2, ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto));
+
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"query",
+ searchSpec,
+ /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
+ /*namespaceMap=*/ImmutableMap.of(),
+ schemaMap,
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+ /*namespaceMap=*/ImmutableMap.of(),
+ schemaMap);
+
+ // All schema should be separated.
+ assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
+ assertThat(resultSpecProto.getResultGroupings(0).getEntryGroupingsList()).hasSize(1);
+ assertThat(resultSpecProto.getResultGroupings(1).getEntryGroupingsList()).hasSize(1);
+ assertThat(resultSpecProto.getResultGroupings(2).getEntryGroupingsList()).hasSize(1);
+ assertThat(resultSpecProto.getResultGroupings(3).getEntryGroupingsList()).hasSize(1);
+ }
+
+ @Test
+ public void testToResultSpecProto_groupByNamespaceAndSchema() throws Exception {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, 5)
+ .build();
+
+ String prefix1 = PrefixUtil.createPrefix("package1", "database");
+ String prefix2 = PrefixUtil.createPrefix("package2", "database");
+ Map<String, Set<String>> namespaceMap = /*namespaceMap=*/ImmutableMap.of(
+ prefix1, ImmutableSet.of(
+ prefix1 + "namespaceA",
+ prefix1 + "namespaceB"),
+ prefix2, ImmutableSet.of(
+ prefix2 + "namespaceA",
+ prefix2 + "namespaceB"));
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+ prefix1, ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2, ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto));
+
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"query",
+ searchSpec,
+ /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
+ namespaceMap,
+ schemaMap,
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
+
+ assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
+ ResultSpecProto.ResultGrouping grouping1 = resultSpecProto.getResultGroupings(0);
+ // Each grouping should have a size of 2.
+ assertThat(grouping1.getEntryGroupingsList()).hasSize(2);
+ // Each grouping should have the same namespace and schema type.
+ // Each entry should have the same package and database.
+ assertThat(grouping1.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceA");
+ assertThat(grouping1.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeA");
+ assertThat(grouping1.getEntryGroupings(1).getNamespace())
+ .isEqualTo("package2$database/namespaceA");
+ assertThat(grouping1.getEntryGroupings(1).getSchema())
+ .isEqualTo("package2$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping2 = resultSpecProto.getResultGroupings(1);
+ // Each grouping should have a size of 2.
+ assertThat(grouping2.getEntryGroupingsList()).hasSize(2);
+ // Each grouping should have the same namespace and schema type.
+ // Each entry should have the same package and database.
+ assertThat(grouping2.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceA");
+ assertThat(grouping2.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeB");
+ assertThat(grouping2.getEntryGroupings(1).getNamespace())
+ .isEqualTo("package2$database/namespaceA");
+ assertThat(grouping2.getEntryGroupings(1).getSchema())
+ .isEqualTo("package2$database/typeB");
+
+ ResultSpecProto.ResultGrouping grouping3 = resultSpecProto.getResultGroupings(2);
+ // Each grouping should have a size of 2.
+ assertThat(grouping3.getEntryGroupingsList()).hasSize(2);
+ // Each grouping should have the same namespace and schema type.
+ // Each entry should have the same package and database.
+ assertThat(grouping3.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceB");
+ assertThat(grouping3.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeA");
+ assertThat(grouping3.getEntryGroupings(1).getNamespace())
+ .isEqualTo("package2$database/namespaceB");
+ assertThat(grouping3.getEntryGroupings(1).getSchema())
+ .isEqualTo("package2$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping4 = resultSpecProto.getResultGroupings(3);
+ // Each grouping should have a size of 2.
+ assertThat(grouping4.getEntryGroupingsList()).hasSize(2);
+ // Each grouping should have the same namespace and schema type.
+ // Each entry should have the same package and database.
+ assertThat(grouping4.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceB");
+ assertThat(grouping4.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeB");
+ assertThat(grouping4.getEntryGroupings(1).getNamespace())
+ .isEqualTo("package2$database/namespaceB");
+ assertThat(grouping4.getEntryGroupings(1).getSchema())
+ .isEqualTo("package2$database/typeB");
+ }
+
+ @Test
+ public void testToResultSpecProto_groupByNamespaceAndSchemaAndPackage() throws Exception {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE
+ | SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, 5)
+ .build();
+ String prefix1 = PrefixUtil.createPrefix("package1", "database");
+ String prefix2 = PrefixUtil.createPrefix("package2", "database");
+ Map<String, Set<String>> namespaceMap = /*namespaceMap=*/ImmutableMap.of(
+ prefix1, ImmutableSet.of(
+ prefix1 + "namespaceA",
+ prefix1 + "namespaceB"),
+ prefix2, ImmutableSet.of(
+ prefix2 + "namespaceA",
+ prefix2 + "namespaceB"));
+ SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+ Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+ prefix1, ImmutableMap.of(
+ prefix1 + "typeA", configProto,
+ prefix1 + "typeB", configProto),
+ prefix2, ImmutableMap.of(
+ prefix2 + "typeA", configProto,
+ prefix2 + "typeB", configProto));
+
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"query",
+ searchSpec,
+ /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
+ namespaceMap,
+ schemaMap,
+ mDefaultIcingOptionsConfig);
+ ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
+
+ assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(8);
+ ResultSpecProto.ResultGrouping grouping1 = resultSpecProto.getResultGroupings(0);
+ //assertThat(grouping1.getEntryGroupingsList()).containsExactly();
+ // Each grouping should have the size of 1.
+ assertThat(grouping1.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping1.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package2$database/namespaceA");
+ assertThat(grouping1.getEntryGroupings(0).getSchema())
+ .isEqualTo("package2$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping2 = resultSpecProto.getResultGroupings(1);
+ // Each grouping should have the size of 1.
+ assertThat(grouping2.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping2.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package2$database/namespaceA");
+ assertThat(grouping2.getEntryGroupings(0).getSchema())
+ .isEqualTo("package2$database/typeB");
+
+ ResultSpecProto.ResultGrouping grouping3 = resultSpecProto.getResultGroupings(2);
+ // Each grouping should have the size of 1.
+ assertThat(grouping3.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping3.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package2$database/namespaceB");
+ assertThat(grouping3.getEntryGroupings(0).getSchema())
+ .isEqualTo("package2$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping4 = resultSpecProto.getResultGroupings(3);
+ // Each grouping should have the size of 1.
+ assertThat(grouping4.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping4.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package2$database/namespaceB");
+ assertThat(grouping4.getEntryGroupings(0).getSchema())
+ .isEqualTo("package2$database/typeB");
+
+ ResultSpecProto.ResultGrouping grouping5 = resultSpecProto.getResultGroupings(4);
+ // Each grouping should have the size of 1.
+ assertThat(grouping5.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping5.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceA");
+ assertThat(grouping5.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping6 = resultSpecProto.getResultGroupings(5);
+ // Each grouping should have the size of 1.
+ assertThat(grouping6.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping6.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceA");
+ assertThat(grouping6.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeB");
+
+ ResultSpecProto.ResultGrouping grouping7 = resultSpecProto.getResultGroupings(6);
+ // Each grouping should have the size of 1.
+ assertThat(grouping7.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping7.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceB");
+ assertThat(grouping7.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeA");
+
+ ResultSpecProto.ResultGrouping grouping8 = resultSpecProto.getResultGroupings(7);
+ // Each grouping should have the size of 1.
+ assertThat(grouping8.getEntryGroupingsList()).hasSize(1);
+ // Each entry should have the same package.
+ assertThat(grouping8.getEntryGroupings(0).getNamespace())
+ .isEqualTo("package1$database/namespaceB");
+ assertThat(grouping8.getEntryGroupings(0).getSchema())
+ .isEqualTo("package1$database/typeB");
+ }
+
+ @Test
public void testGetTargetNamespaceFilters_emptySearchingFilter() {
SearchSpec searchSpec = new SearchSpec.Builder().build();
String prefix1 = PrefixUtil.createPrefix("package", "database1");
@@ -372,7 +816,8 @@
/*queryExpression=*/"",
searchSpec,
/*prefixes=*/ImmutableSet.of(prefix1, prefix2),
- namespaceMap, /*schemaMap=*/ImmutableMap.of());
+ namespaceMap, /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -397,7 +842,8 @@
"package$database1/namespace2"),
prefix2, ImmutableSet.of("package$database2/namespace3",
"package$database2/namespace4")),
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Only search prefix1 will return namespace 1 and 2.
@@ -419,7 +865,8 @@
/*namespaceMap=*/ImmutableMap.of(
prefix1, ImmutableSet.of("package$database1/namespace1",
"package$database1/namespace2")),
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching namespace filter is not empty, the target namespace filter will be the
// intersection of the searching namespace filters that users want to search over and
@@ -442,7 +889,8 @@
/*namespaceMap=*/ImmutableMap.of(
prefix1, ImmutableSet.of("package$database1/namespace1",
"package$database1/namespace2")),
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching namespace filter is not empty, the target namespace filter will be the
// intersection of the searching namespace filters that users want to search over and
@@ -469,7 +917,8 @@
"package$database1/typeB", schemaTypeConfigProto),
prefix2, ImmutableMap.of(
"package$database2/typeC", schemaTypeConfigProto,
- "package$database2/typeD", schemaTypeConfigProto)));
+ "package$database2/typeD", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Empty searching filter will get all types for target filter
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
@@ -497,7 +946,8 @@
"package$database1/typeB", schemaTypeConfigProto),
prefix2, ImmutableMap.of(
"package$database2/typeC", schemaTypeConfigProto,
- "package$database2/typeD", schemaTypeConfigProto)));
+ "package$database2/typeD", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// Only search prefix1 will return typeA and B.
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
@@ -521,7 +971,8 @@
/*schemaMap=*/ImmutableMap.of(
prefix1, ImmutableMap.of(
"package$database1/typeA", schemaTypeConfigProto,
- "package$database1/typeB", schemaTypeConfigProto)));
+ "package$database1/typeB", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If the searching schema filter is not empty, the target schema filter will be the
// intersection of the schema filters that users want to search over and those candidates
@@ -547,7 +998,8 @@
/*schemaMap=*/ImmutableMap.of(
prefix1, ImmutableMap.of(
"package$database1/typeA", schemaTypeConfigProto,
- "package$database1/typeB", schemaTypeConfigProto)));
+ "package$database1/typeB", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
// If there is no intersection of the schema filters that user want to search over and
// those filters which are stored in AppSearch, return empty.
@@ -556,20 +1008,19 @@
@Test
public void testRemoveInaccessibleSchemaFilter() throws Exception {
- AppSearchImpl appSearchImpl = AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/null,
- ALWAYS_OPTIMIZE,
- /*visibilityChecker=*/null);
- VisibilityStore visibilityStore = new VisibilityStore(appSearchImpl);
+ VisibilityStore visibilityStore = new VisibilityStore(mAppSearchImpl);
final String prefix = PrefixUtil.createPrefix("package", "database");
SchemaTypeConfigProto schemaTypeConfigProto =
SchemaTypeConfigProto.newBuilder().getDefaultInstanceForType();
+
+ SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
+ JoinSpec joinSpec = new JoinSpec.Builder("entity")
+ .setNestedSearch("", nestedSearchSpec).build();
+
SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
/*queryExpression=*/"",
- new SearchSpec.Builder().build(),
+ new SearchSpec.Builder().setJoinSpec(joinSpec).build(),
/*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/ImmutableMap.of(
prefix, ImmutableSet.of("package$database/namespace1")),
@@ -577,7 +1028,8 @@
prefix, ImmutableMap.of(
"package$database/schema1", schemaTypeConfigProto,
"package$database/schema2", schemaTypeConfigProto,
- "package$database/schema3", schemaTypeConfigProto)));
+ "package$database/schema3", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
converter.removeInaccessibleSchemaFilter(
new CallerAccess(/*callingPackageName=*/"otherPackageName"),
@@ -590,12 +1042,21 @@
// schema 2 is filtered out since it is not searchable for user.
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
prefix + "schema1", prefix + "schema3");
+
+ SearchSpecProto nestedSearchProto =
+ searchSpecProto.getJoinSpec().getNestedSpec().getSearchSpec();
+ assertThat(nestedSearchProto.getSchemaTypeFiltersList()).containsExactly(
+ prefix + "schema1", prefix + "schema3");
}
@Test
public void testIsNothingToSearch() {
String prefix = PrefixUtil.createPrefix("package", "database");
+ SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
+ JoinSpec joinSpec = new JoinSpec.Builder("entity")
+ .setNestedSearch("nested", nestedSearchSpec).build();
SearchSpec searchSpec = new SearchSpec.Builder()
+ .setJoinSpec(joinSpec)
.addFilterSchemas("schema").addFilterNamespaces("namespace").build();
// build maps
@@ -614,7 +1075,8 @@
/*queryExpression=*/"",
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/namespaceMap,
- /*schemaMap=*/ImmutableMap.of());
+ /*schemaMap=*/ImmutableMap.of(),
+ mDefaultIcingOptionsConfig);
assertThat(emptySchemaConverter.hasNothingToSearch()).isTrue();
SearchSpecToProtoConverter emptyNamespaceConverter =
@@ -622,14 +1084,16 @@
/*queryExpression=*/"",
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
/*namespaceMap=*/ImmutableMap.of(),
- schemaMap);
+ schemaMap,
+ mDefaultIcingOptionsConfig);
assertThat(emptyNamespaceConverter.hasNothingToSearch()).isTrue();
SearchSpecToProtoConverter nonEmptyConverter =
new SearchSpecToProtoConverter(
/*queryExpression=*/"",
searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
- namespaceMap, schemaMap);
+ namespaceMap, schemaMap,
+ mDefaultIcingOptionsConfig);
assertThat(nonEmptyConverter.hasNothingToSearch()).isFalse();
// remove all target schema filter, and the query becomes nothing to search.
@@ -638,6 +1102,49 @@
/*visibilityStore=*/null,
/*visibilityChecker=*/null);
assertThat(nonEmptyConverter.hasNothingToSearch()).isTrue();
+ // As the JoinSpec has nothing to search, it should not be part of the SearchSpec
+ assertThat(nonEmptyConverter.toSearchSpecProto().hasJoinSpec()).isFalse();
+ }
+
+ @Test
+ public void testRemoveInaccessibleSchemaFilterWithEmptyNestedFilter() throws Exception {
+ VisibilityStore visibilityStore = new VisibilityStore(mAppSearchImpl);
+
+ final String prefix = PrefixUtil.createPrefix("package", "database");
+ SchemaTypeConfigProto schemaTypeConfigProto =
+ SchemaTypeConfigProto.newBuilder().getDefaultInstanceForType();
+
+ SearchSpec nestedSearchSpec = new SearchSpec.Builder()
+ .addFilterSchemas(ImmutableSet.of(prefix + "schema1", prefix + "schema2"))
+ .build();
+ JoinSpec joinSpec = new JoinSpec.Builder("entity")
+ .setNestedSearch("nested", nestedSearchSpec).build();
+
+ SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+ /*queryExpression=*/"",
+ new SearchSpec.Builder().setJoinSpec(joinSpec).build(),
+ /*prefixes=*/ImmutableSet.of(prefix),
+ /*namespaceMap=*/ImmutableMap.of(
+ prefix, ImmutableSet.of("package$database/namespace1")),
+ /*schemaMap=*/ImmutableMap.of(
+ prefix, ImmutableMap.of(
+ "package$database/schema1", schemaTypeConfigProto,
+ "package$database/schema2", schemaTypeConfigProto,
+ "package$database/schema3", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
+
+ converter.removeInaccessibleSchemaFilter(
+ new CallerAccess(/*callingPackageName=*/"otherPackageName"),
+ visibilityStore,
+ AppSearchTestUtils.createMockVisibilityChecker(
+ /*visiblePrefixedSchemas=*/ ImmutableSet.of(prefix + "schema3")));
+
+ SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
+ assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(prefix + "schema3");
+
+ // Schema 1 and 2 are filtered out of the nested spec. As the JoinSpec has nothing to
+ // search, it should not be part of the SearchSpec.
+ assertThat(searchSpecProto.hasJoinSpec()).isFalse();
}
@Test
@@ -671,7 +1178,8 @@
/*queryExpression=*/"",
searchSpec, /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
namespaceMap,
- schemaTypeMap);
+ schemaTypeMap,
+ mDefaultIcingOptionsConfig);
TypePropertyWeights expectedTypePropertyWeight1 =
TypePropertyWeights.newBuilder().setSchemaType(prefix1 + schemaTypeA)
@@ -721,7 +1229,8 @@
ImmutableSet.of(prefix1 + "namespace1")),
/*schemaMap=*/ImmutableMap.of(
prefix1,
- ImmutableMap.of(prefix1 + "typeA", schemaTypeConfigProto)));
+ ImmutableMap.of(prefix1 + "typeA", schemaTypeConfigProto)),
+ mDefaultIcingOptionsConfig);
ScoringSpecProto convertedScoringSpecProto = converter.toScoringSpecProto();
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
index fc8d058..167ed1c 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
@@ -32,6 +32,7 @@
import androidx.appsearch.app.PackageIdentifier;
import androidx.appsearch.app.VisibilityDocument;
import androidx.appsearch.localstorage.AppSearchImpl;
+import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -127,7 +128,7 @@
// Persist to disk and re-open the AppSearchImpl
appSearchImplInV0.close();
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile, new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
+ new DefaultIcingOptionsConfig(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
VisibilityDocument actualDocument1 = new VisibilityDocument(
@@ -159,6 +160,7 @@
.build();
assertThat(actualDocument1).isEqualTo(expectedDocument1);
assertThat(actualDocument2).isEqualTo(expectedDocument2);
+ appSearchImpl.close();
}
/** Build AppSearchImpl with deprecated visibility schemas version 0. */
@@ -192,7 +194,7 @@
.build();
// Set deprecated visibility schema version 0 into AppSearchImpl.
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile, new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
+ new DefaultIcingOptionsConfig(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
InternalSetSchemaResponse internalSetSchemaResponse = appSearchImpl.setSchema(
VisibilityStore.VISIBILITY_PACKAGE_NAME,
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
index 37d8fb6..0ca32c0 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
@@ -27,6 +27,7 @@
import androidx.appsearch.app.SetSchemaRequest;
import androidx.appsearch.app.VisibilityDocument;
import androidx.appsearch.localstorage.AppSearchImpl;
+import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -71,7 +72,7 @@
// Create AppSearchImpl with visibility document version 1;
AppSearchImpl appSearchImplInV1 = AppSearchImpl.create(mFile, new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
+ new DefaultIcingOptionsConfig(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
InternalSetSchemaResponse internalSetSchemaResponse = appSearchImplInV1.setSchema(
VisibilityStore.VISIBILITY_PACKAGE_NAME,
@@ -119,7 +120,7 @@
// Persist to disk and re-open the AppSearchImpl
appSearchImplInV1.close();
AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile, new UnlimitedLimitConfig(),
- /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
+ new DefaultIcingOptionsConfig(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
VisibilityDocument actualDocument = new VisibilityDocument(
@@ -140,5 +141,6 @@
ImmutableSet.of(SetSchemaRequest.READ_SMS, SetSchemaRequest.READ_CALENDAR),
ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA),
ImmutableSet.of(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA)));
+ appSearchImpl.close();
}
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
index 0a5364f..99a1ffe 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
@@ -26,6 +26,7 @@
import androidx.appsearch.app.VisibilityDocument;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.localstorage.AppSearchImpl;
+import androidx.appsearch.localstorage.DefaultIcingOptionsConfig;
import androidx.appsearch.localstorage.OptimizeStrategy;
import androidx.appsearch.localstorage.UnlimitedLimitConfig;
import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -60,6 +61,7 @@
mAppSearchImpl = AppSearchImpl.create(
mAppSearchDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
/*initStatsBuilder=*/ null,
ALWAYS_OPTIMIZE,
/*visibilityChecker=*/null);
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
index 2eba5ee..cf8c77d 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
@@ -48,6 +48,8 @@
// fall through
case Features.LIST_FILTER_QUERY_LANGUAGE:
// fall through
+ case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
+ // fall through
case Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH:
// fall through
case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
@@ -55,6 +57,10 @@
case Features.TOKENIZER_TYPE_RFC822:
// fall through
case Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
+ // fall through
+ case Features.SEARCH_SUGGESTION:
+ // fall through
+ case Features.SCHEMA_SET_DELETION_PROPAGATION:
return true;
default:
return false;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index f445af7..ea41a55 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -182,6 +182,7 @@
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
private final OptimizeStrategy mOptimizeStrategy;
private final LimitConfig mLimitConfig;
+ private final IcingOptionsConfig mIcingOptionsConfig;
@GuardedBy("mReadWriteLock")
@VisibleForTesting
@@ -268,12 +269,13 @@
public static AppSearchImpl create(
@NonNull File icingDir,
@NonNull LimitConfig limitConfig,
+ @NonNull IcingOptionsConfig icingOptionsConfig,
@Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy,
@Nullable VisibilityChecker visibilityChecker)
throws AppSearchException {
- return new AppSearchImpl(icingDir, limitConfig, initStatsBuilder, optimizeStrategy,
- visibilityChecker);
+ return new AppSearchImpl(icingDir, limitConfig, icingOptionsConfig, initStatsBuilder,
+ optimizeStrategy, visibilityChecker);
}
/**
@@ -282,12 +284,14 @@
private AppSearchImpl(
@NonNull File icingDir,
@NonNull LimitConfig limitConfig,
+ @NonNull IcingOptionsConfig icingOptionsConfig,
@Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy,
@Nullable VisibilityChecker visibilityChecker)
throws AppSearchException {
Preconditions.checkNotNull(icingDir);
mLimitConfig = Preconditions.checkNotNull(limitConfig);
+ mIcingOptionsConfig = Preconditions.checkNotNull(icingOptionsConfig);
mOptimizeStrategy = Preconditions.checkNotNull(optimizeStrategy);
mVisibilityCheckerLocked = visibilityChecker;
@@ -296,7 +300,19 @@
// We synchronize here because we don't want to call IcingSearchEngine.initialize() more
// than once. It's unnecessary and can be a costly operation.
IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
- .setBaseDir(icingDir.getAbsolutePath()).build();
+ .setBaseDir(icingDir.getAbsolutePath())
+ .setMaxTokenLength(icingOptionsConfig.getMaxTokenLength())
+ .setIndexMergeSize(icingOptionsConfig.getIndexMergeSize())
+ .setDocumentStoreNamespaceIdFingerprint(
+ icingOptionsConfig.getDocumentStoreNamespaceIdFingerprint())
+ .setOptimizeRebuildIndexThreshold(
+ icingOptionsConfig.getOptimizeRebuildIndexThreshold())
+ .setCompressionLevel(icingOptionsConfig.getCompressionLevel())
+ .setAllowCircularSchemaDefinitions(
+ icingOptionsConfig.getAllowCircularSchemaDefinitions())
+ .setPreMappingFbv(icingOptionsConfig.getUsePreMappingWithFileBackedVector())
+ .setUsePersistentHashMap(icingOptionsConfig.getUsePersistentHashMap())
+ .build();
LogUtil.piiTrace(TAG, "Constructing IcingSearchEngine, request", options);
mIcingSearchEngineLocked = new IcingSearchEngine(options);
LogUtil.piiTrace(
@@ -1327,7 +1343,8 @@
String prefix = createPrefix(packageName, databaseName);
SearchSpecToProtoConverter searchSpecToProtoConverter =
new SearchSpecToProtoConverter(queryExpression, searchSpec,
- Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked);
+ Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked,
+ mIcingOptionsConfig);
if (searchSpecToProtoConverter.hasNothingToSearch()) {
// there is nothing to search over given their search filters, so we can return an
// empty SearchResult and skip sending request to Icing.
@@ -1409,7 +1426,7 @@
}
SearchSpecToProtoConverter searchSpecToProtoConverter =
new SearchSpecToProtoConverter(queryExpression, searchSpec, prefixFilters,
- mNamespaceMapLocked, mSchemaMapLocked);
+ mNamespaceMapLocked, mSchemaMapLocked, mIcingOptionsConfig);
// Remove those inaccessible schemas.
searchSpecToProtoConverter.removeInaccessibleSchemaFilter(
callerAccess, mVisibilityStoreLocked, mVisibilityCheckerLocked);
@@ -1450,7 +1467,7 @@
long rewriteSearchSpecLatencyStartMillis = SystemClock.elapsedRealtime();
SearchSpecProto finalSearchSpec = searchSpecToProtoConverter.toSearchSpecProto();
ResultSpecProto finalResultSpec = searchSpecToProtoConverter.toResultSpecProto(
- mNamespaceMapLocked);
+ mNamespaceMapLocked, mSchemaMapLocked);
ScoringSpecProto scoringSpec = searchSpecToProtoConverter.toScoringSpecProto();
if (sStatsBuilder != null) {
sStatsBuilder.setRewriteSearchSpecLatencyMillis((int)
@@ -1492,6 +1509,10 @@
TAG, "search, response", searchResultProto.getResultsCount(), searchResultProto);
if (sStatsBuilder != null) {
sStatsBuilder.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()));
+ if (searchSpec.hasJoinSpec()) {
+ sStatsBuilder.setJoinType(AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+ }
AppSearchLoggerHelper.copyNativeStats(searchResultProto.getQueryStats(), sStatsBuilder);
}
checkSuccess(searchResultProto.getStatus());
@@ -1629,6 +1650,8 @@
if (sStatsBuilder != null) {
sStatsBuilder.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()));
+ // Join query stats are handled by SearchResultsImpl, which has access to the
+ // original SearchSpec.
AppSearchLoggerHelper.copyNativeStats(searchResultProto.getQueryStats(),
sStatsBuilder);
}
@@ -1868,7 +1891,8 @@
SearchSpecToProtoConverter searchSpecToProtoConverter =
new SearchSpecToProtoConverter(queryExpression, searchSpec,
- Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked);
+ Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked,
+ mIcingOptionsConfig);
if (searchSpecToProtoConverter.hasNothingToSearch()) {
// there is nothing to search over given their search filters, so we can return
// early and skip sending request to Icing.
@@ -2218,6 +2242,9 @@
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
+ if (LogUtil.DEBUG) {
+ Log.d(TAG, "Clear data for package: " + packageName);
+ }
// TODO(b/193494000): We are calling getPackageToDatabases here and in several other
// places within AppSearchImpl. This method is not efficient and does a lot of string
// manipulation. We should find a way to cache the package to database map so it can
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
index 4772afe..0189673 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
@@ -130,7 +130,10 @@
.setJavaToNativeJniLatencyMillis(
fromNativeStats.getJavaToNativeJniLatencyMs())
.setNativeToJavaJniLatencyMillis(
- fromNativeStats.getNativeToJavaJniLatencyMs());
+ fromNativeStats.getNativeToJavaJniLatencyMs())
+ .setNativeNumJoinedResultsCurrentPage(
+ fromNativeStats.getNumJoinedResultsReturnedCurrentPage())
+ .setNativeJoinLatencyMillis(fromNativeStats.getJoinLatencyMs());
}
/**
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java
new file mode 100644
index 0000000..80d2c85
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/DefaultIcingOptionsConfig.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/DefaultIcingOptionsConfig.java)
+package androidx.appsearch.localstorage;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Icing options for AppSearch local-storage. Note, these values are not necessarily the defaults
+ * set in {@link com.google.android.icing.proto.IcingSearchEngineOptions} proto.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class DefaultIcingOptionsConfig implements IcingOptionsConfig {
+ @Override
+ public int getMaxTokenLength() {
+ return DEFAULT_MAX_TOKEN_LENGTH;
+ }
+
+ @Override
+ public int getIndexMergeSize() {
+ return DEFAULT_INDEX_MERGE_SIZE;
+ }
+
+ @Override
+ public boolean getDocumentStoreNamespaceIdFingerprint() {
+ return true;
+ }
+
+ @Override
+ public float getOptimizeRebuildIndexThreshold() {
+ return 0.9f;
+ }
+
+ @Override
+ public int getCompressionLevel() {
+ return DEFAULT_COMPRESSION_LEVEL;
+ }
+
+ @Override
+ public boolean getAllowCircularSchemaDefinitions() {
+ return true;
+ }
+
+ @Override
+ public boolean getUseReadOnlySearch() {
+ return true;
+ }
+
+ @Override
+ public boolean getUsePreMappingWithFileBackedVector() {
+ return true;
+ }
+
+ @Override
+ public boolean getUsePersistentHashMap() {
+ return true;
+ }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java
new file mode 100644
index 0000000..16e7c04
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.localstorage;
+
+import androidx.annotation.RestrictTo;
+
+import com.google.android.icing.proto.IcingSearchEngineOptions;
+
+/**
+ * An interface exposing the optional config flags in {@link IcingSearchEngineOptions} used to
+ * instantiate {@link com.google.android.icing.IcingSearchEngine}, as well as other additional
+ * config flags for IcingSearchEngine.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface IcingOptionsConfig {
+ // Defaults from IcingSearchEngineOptions proto
+ int DEFAULT_MAX_TOKEN_LENGTH = 30;
+
+ int DEFAULT_INDEX_MERGE_SIZE = 1048576; // 1 MiB
+
+ boolean DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT = false;
+
+ float DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD = 0.0f;
+
+ /**
+ * The default compression level in IcingSearchEngineOptions proto matches the
+ * previously-hardcoded document compression level in Icing (which is 3).
+ */
+ int DEFAULT_COMPRESSION_LEVEL = 3;
+
+ boolean DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR = false;
+
+ boolean DEFAULT_USE_PERSISTENT_HASH_MAP = false;
+
+
+ /**
+ * The maximum allowable token length. All tokens in excess of this size will be truncated to
+ * max_token_length before being indexed.
+ *
+ * <p>Clients may use this option to prevent unnecessary indexing of long tokens.
+ * Depending on the use case, indexing all of
+ * 'Supercalifragilisticexpialidocious' may be unnecessary - a user is
+ * unlikely to type that entire query. So only indexing the first n bytes may
+ * still provide the desired behavior without wasting resources.
+ */
+ int getMaxTokenLength();
+
+ /**
+ * The size (measured in bytes) at which Icing's internal indices should be
+ * merged. Icing buffers changes together before merging them into a more
+ * compact format. When the buffer exceeds index_merge_size during a Put
+ * operation, the buffer is merged into the larger, more compact index.
+ *
+ * <p>This more compact index is more efficient to search over as the index
+ * grows larger and has smaller system health impact.
+ *
+ * <p>Setting a low index_merge_size increases the frequency of merges -
+ * increasing indexing-time latency and flash wear. Setting a high
+ * index_merge_size leads to larger resource usage and higher query latency.
+ */
+ int getIndexMergeSize();
+
+ /**
+ * Whether to use namespace id or namespace name to build up fingerprint for
+ * document_key_mapper_ and corpus_mapper_ in document store.
+ */
+ boolean getDocumentStoreNamespaceIdFingerprint();
+
+ /**
+ * The threshold of the percentage of invalid documents at which to rebuild index
+ * during optimize.
+ *
+ * <p>We rebuild index if and only if |invalid_documents| / |all_documents| >= threshold.
+ *
+ * <p>Rebuilding the index could be faster than optimizing the index if we have
+ * removed most of the documents. Based on benchmarks, 85%~95% seems to be a good threshold
+ * for most cases.
+ */
+ float getOptimizeRebuildIndexThreshold();
+
+ /**
+ * The level of gzip compression for documents in the Icing document store.
+ *
+ * <p>NO_COMPRESSION = 0, BEST_SPEED = 1, BEST_COMPRESSION = 9
+ */
+ int getCompressionLevel();
+
+ /**
+ * Whether to allow circular references between schema types for the schema definition.
+ *
+ * <p>Even when set to true, circular references are still not allowed in the following cases:
+ * 1. All edges of a cycle have index_nested_properties=true
+ * 2. One of the types in the cycle has a joinable property, or depends on a type with a
+ * joinable property.
+ */
+ boolean getAllowCircularSchemaDefinitions();
+
+ /**
+ * Flag for {@link com.google.android.icing.proto.SearchSpecProto}.
+ *
+ * <p>Whether to use the read-only implementation of IcingSearchEngine::Search.
+ *
+ * <p>The read-only version enables multiple queries to be performed concurrently
+ * as it only acquires the read lock at IcingSearchEngine's level. Finer-grained locks are
+ * implemented around code paths that write changes to Icing during Search.
+ */
+ boolean getUseReadOnlySearch();
+
+ /**
+ * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}.
+ *
+ * <p>Whether or not to pre-map the potential memory region used by the PersistentHashMap.
+ * This will avoid the need to re-map the mmapping used by PersistentHashMap whenever the
+ * underlying storage grows.
+ */
+ boolean getUsePreMappingWithFileBackedVector();
+
+ /**
+ * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}.
+ *
+ * <p>Whether or not to use the PersistentHashMap in the QualifiedIdTypeJoinableIndex. If false,
+ * we will use the old IcingDynamicTrie to store key value pairs.
+ */
+ boolean getUsePersistentHashMap();
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/JetpackOptimizeStrategy.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/JetpackOptimizeStrategy.java
index 6c0ee68..f17b3ce 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/JetpackOptimizeStrategy.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/JetpackOptimizeStrategy.java
@@ -29,7 +29,7 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class JetpackOptimizeStrategy implements OptimizeStrategy{
+public class JetpackOptimizeStrategy implements OptimizeStrategy {
@VisibleForTesting
static final int DOC_COUNT_OPTIMIZE_THRESHOLD = 1000;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index b862ce3..758e63b 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -340,6 +340,7 @@
mAppSearchImpl = AppSearchImpl.create(
icingDir,
new UnlimitedLimitConfig(),
+ new DefaultIcingOptionsConfig(),
initStatsBuilder,
new JetpackOptimizeStrategy(),
/*visibilityChecker=*/null);
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
index 8da40bb..11b4813 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appsearch.app.AppSearchSchema;
import androidx.appsearch.app.SearchResult;
import androidx.appsearch.app.SearchResultPage;
import androidx.appsearch.app.SearchResults;
@@ -114,6 +115,11 @@
searchResultPage = mAppSearchImpl.getNextPage(mPackageName, mNextPageToken,
sStatsBuilder);
if (mLogger != null && sStatsBuilder != null) {
+ if (mSearchSpec.getJoinSpec() != null
+ && !mSearchSpec.getJoinSpec().getChildPropertyExpression().isEmpty()) {
+ sStatsBuilder.setJoinType(AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+ }
mLogger.logStats(sStatsBuilder.build());
}
}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/UnlimitedLimitConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/UnlimitedLimitConfig.java
index b4f37fa..a402969 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/UnlimitedLimitConfig.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/UnlimitedLimitConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+// @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/UnlimitedLimitConfig.java)
package androidx.appsearch.localstorage;
import androidx.annotation.RestrictTo;
@@ -22,7 +22,6 @@
* In Jetpack, AppSearch doesn't enforce artificial limits on number of documents or size of
* documents, since the app is the only user of the Icing instance. Icing still enforces a docid
* limit of 1M docs.
- * @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class UnlimitedLimitConfig implements LimitConfig {
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
index b22d33f..976f0aa 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
@@ -99,6 +99,7 @@
.setValueType(
convertJoinableValueTypeToProto(
stringProperty.getJoinableValueType()))
+ .setPropagateDelete(stringProperty.getDeletionPropagation())
.build();
builder.setJoinableConfig(joinableConfig);
}
@@ -188,6 +189,7 @@
.setJoinableValueType(
convertJoinableValueTypeFromProto(
proto.getJoinableConfig().getValueType()))
+ .setDeletionPropagation(proto.getJoinableConfig().getPropagateDelete())
.setTokenizerType(
proto.getStringIndexingConfig().getTokenizerType().getNumber());
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
index 0e80ebe..61e919b 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
@@ -18,6 +18,7 @@
import static androidx.appsearch.localstorage.util.PrefixUtil.createPrefix;
import static androidx.appsearch.localstorage.util.PrefixUtil.getPackageName;
+import static androidx.appsearch.localstorage.util.PrefixUtil.getPrefix;
import static androidx.appsearch.localstorage.util.PrefixUtil.removePrefix;
import android.util.Log;
@@ -26,8 +27,10 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.appsearch.app.JoinSpec;
+import androidx.appsearch.app.SearchResult;
import androidx.appsearch.app.SearchSpec;
import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.localstorage.IcingOptionsConfig;
import androidx.appsearch.localstorage.visibilitystore.CallerAccess;
import androidx.appsearch.localstorage.visibilitystore.VisibilityChecker;
import androidx.appsearch.localstorage.visibilitystore.VisibilityStore;
@@ -79,6 +82,7 @@
* filters which are stored in AppSearch. This is a field so that we can generate nested protos.
*/
private final Map<String, Set<String>> mNamespaceMap;
+
/**
*The cached Map of {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>} stores all
* prefixed schema filters which are stored inAppSearch. This is a field so that we can
@@ -87,6 +91,18 @@
private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMap;
/**
+ * Optional config flags in {@link SearchSpecProto}.
+ */
+ private final IcingOptionsConfig mIcingOptionsConfig;
+
+ /**
+ * The nested converter, which contains SearchSpec, ResultSpec, and ScoringSpec information
+ * about the nested query. This will remain null if there is no nested {@link JoinSpec}.
+ */
+ @Nullable
+ private SearchSpecToProtoConverter mNestedConverter = null;
+
+ /**
* Creates a {@link SearchSpecToProtoConverter} for given {@link SearchSpec}.
*
* @param queryExpression Query String to search.
@@ -102,12 +118,14 @@
@NonNull SearchSpec searchSpec,
@NonNull Set<String> prefixes,
@NonNull Map<String, Set<String>> namespaceMap,
- @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+ @NonNull IcingOptionsConfig icingOptionsConfig) {
mQueryExpression = Preconditions.checkNotNull(queryExpression);
mSearchSpec = Preconditions.checkNotNull(searchSpec);
mPrefixes = Preconditions.checkNotNull(prefixes);
mNamespaceMap = Preconditions.checkNotNull(namespaceMap);
mSchemaMap = Preconditions.checkNotNull(schemaMap);
+ mIcingOptionsConfig = Preconditions.checkNotNull(icingOptionsConfig);
mTargetPrefixedNamespaceFilters =
SearchSpecToProtoConverterUtil.generateTargetNamespaceFilters(
prefixes, namespaceMap, searchSpec.getFilterNamespaces());
@@ -120,11 +138,28 @@
} else {
mTargetPrefixedSchemaFilters = new ArraySet<>();
}
+
+ JoinSpec joinSpec = searchSpec.getJoinSpec();
+ if (joinSpec == null) {
+ return;
+ }
+
+ mNestedConverter = new SearchSpecToProtoConverter(
+ joinSpec.getNestedQuery(),
+ joinSpec.getNestedSearchSpec(),
+ mPrefixes,
+ namespaceMap,
+ schemaMap,
+ mIcingOptionsConfig);
}
/**
* @return whether this search's target filters are empty. If any target filter is empty, we
* should skip send request to Icing.
+ *
+ * <p> The nestedConverter is not checked as {@link SearchResult}s from the nested query have
+ * to be joined to a {@link SearchResult} from the parent query. If the parent query has
+ * nothing to search, then so does the child query.
*/
public boolean hasNothingToSearch() {
return mTargetPrefixedNamespaceFilters.isEmpty() || mTargetPrefixedSchemaFilters.isEmpty();
@@ -146,23 +181,68 @@
@NonNull CallerAccess callerAccess,
@Nullable VisibilityStore visibilityStore,
@Nullable VisibilityChecker visibilityChecker) {
+ removeInaccessibleSchemaFilterCached(callerAccess, visibilityStore,
+ /*inaccessibleSchemaPrefixes=*/new ArraySet<>(),
+ /*accessibleSchemaPrefixes=*/new ArraySet<>(), visibilityChecker);
+ }
+
+ /**
+ * For each target schema, we will check visibility store is that accessible to the caller. And
+ * remove this schemas if it is not allowed for caller to query. This private version accepts
+ * two additional parameters to minimize the amount of calls to
+ * {@link VisibilityUtil#isSchemaSearchableByCaller}.
+ *
+ * @param callerAccess Visibility access info of the calling app
+ * @param visibilityStore The {@link VisibilityStore} that store all visibility
+ * information.
+ * @param visibilityChecker Optional visibility checker to check whether the caller
+ * could access target schemas. Pass {@code null} will
+ * reject access for all documents which doesn't belong
+ * to the calling package.
+ * @param inaccessibleSchemaPrefixes A set of schemas that are known to be inaccessible. This
+ * is helpful for reducing duplicate calls to
+ * {@link VisibilityUtil}.
+ * @param accessibleSchemaPrefixes A set of schemas that are known to be accessible. This is
+ * helpful for reducing duplicate calls to
+ * {@link VisibilityUtil}.
+ */
+ private void removeInaccessibleSchemaFilterCached(
+ @NonNull CallerAccess callerAccess,
+ @Nullable VisibilityStore visibilityStore,
+ @NonNull Set<String> inaccessibleSchemaPrefixes,
+ @NonNull Set<String> accessibleSchemaPrefixes,
+ @Nullable VisibilityChecker visibilityChecker) {
Iterator<String> targetPrefixedSchemaFilterIterator =
mTargetPrefixedSchemaFilters.iterator();
while (targetPrefixedSchemaFilterIterator.hasNext()) {
String targetPrefixedSchemaFilter = targetPrefixedSchemaFilterIterator.next();
String packageName = getPackageName(targetPrefixedSchemaFilter);
- if (!VisibilityUtil.isSchemaSearchableByCaller(
+ if (accessibleSchemaPrefixes.contains(targetPrefixedSchemaFilter)) {
+ continue;
+ } else if (inaccessibleSchemaPrefixes.contains(targetPrefixedSchemaFilter)) {
+ targetPrefixedSchemaFilterIterator.remove();
+ } else if (!VisibilityUtil.isSchemaSearchableByCaller(
callerAccess,
packageName,
targetPrefixedSchemaFilter,
visibilityStore,
visibilityChecker)) {
targetPrefixedSchemaFilterIterator.remove();
+ inaccessibleSchemaPrefixes.add(targetPrefixedSchemaFilter);
+ } else {
+ accessibleSchemaPrefixes.add(targetPrefixedSchemaFilter);
}
}
+
+ if (mNestedConverter != null) {
+ mNestedConverter.removeInaccessibleSchemaFilterCached(
+ callerAccess, visibilityStore, inaccessibleSchemaPrefixes,
+ accessibleSchemaPrefixes, visibilityChecker);
+ }
}
+
/** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */
@NonNull
public SearchSpecProto toSearchSpecProto() {
@@ -172,7 +252,8 @@
SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder()
.setQuery(mQueryExpression)
.addAllNamespaceFilters(mTargetPrefixedNamespaceFilters)
- .addAllSchemaTypeFilters(mTargetPrefixedSchemaFilters);
+ .addAllSchemaTypeFilters(mTargetPrefixedSchemaFilters)
+ .setUseReadOnlySearch(mIcingOptionsConfig.getUseReadOnlySearch());
@SearchSpec.TermMatch int termMatchCode = mSearchSpec.getTermMatch();
TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
@@ -181,25 +262,25 @@
}
protoBuilder.setTermMatchType(termMatchCodeProto);
- JoinSpec joinSpec = mSearchSpec.getJoinSpec();
- if (joinSpec != null) {
- SearchSpecToProtoConverter nestedConverter = new SearchSpecToProtoConverter(
- joinSpec.getNestedQuery(), joinSpec.getNestedSearchSpec(), mPrefixes,
- mNamespaceMap, mSchemaMap);
+ if (mNestedConverter != null && !mNestedConverter.hasNothingToSearch()) {
+ JoinSpecProto.NestedSpecProto nestedSpec =
+ JoinSpecProto.NestedSpecProto.newBuilder()
+ .setResultSpec(mNestedConverter.toResultSpecProto(
+ mNamespaceMap, mSchemaMap))
+ .setScoringSpec(mNestedConverter.toScoringSpecProto())
+ .setSearchSpec(mNestedConverter.toSearchSpecProto())
+ .build();
- JoinSpecProto.NestedSpecProto nestedSpec = JoinSpecProto.NestedSpecProto.newBuilder()
- .setResultSpec(nestedConverter.toResultSpecProto(mNamespaceMap))
- .setScoringSpec(nestedConverter.toScoringSpecProto())
- .setSearchSpec(nestedConverter.toSearchSpecProto())
- .build();
-
- JoinSpecProto.Builder joinSpecProtoBuilder = JoinSpecProto.newBuilder()
- .setNestedSpec(nestedSpec)
- .setParentPropertyExpression(JoinSpec.QUALIFIED_ID)
- .setChildPropertyExpression(joinSpec.getChildPropertyExpression())
- .setAggregationScoringStrategy(
- toAggregationScoringStrategy(joinSpec.getAggregationScoringStrategy()))
- .setMaxJoinedChildCount(joinSpec.getMaxJoinedResultCount());
+ // This cannot be null, otherwise mNestedConverter would be null as well.
+ JoinSpec joinSpec = mSearchSpec.getJoinSpec();
+ JoinSpecProto.Builder joinSpecProtoBuilder =
+ JoinSpecProto.newBuilder()
+ .setNestedSpec(nestedSpec)
+ .setParentPropertyExpression(JoinSpec.QUALIFIED_ID)
+ .setChildPropertyExpression(joinSpec.getChildPropertyExpression())
+ .setAggregationScoringStrategy(
+ toAggregationScoringStrategy(
+ joinSpec.getAggregationScoringStrategy()));
protoBuilder.setJoinSpec(joinSpecProtoBuilder);
}
@@ -252,10 +333,14 @@
*
* @param namespaceMap The cached Map of {@code <Prefix, Set<PrefixedNamespace>>} stores
* all existing prefixed namespace.
+ * @param schemaMap The cached Map of {@code <Prefix, Map<PrefixedSchemaType,
+ * schemaProto>>} stores all prefixed schema filters which are stored
+ * inAppSearch.
*/
@NonNull
public ResultSpecProto toResultSpecProto(
- @NonNull Map<String, Set<String>> namespaceMap) {
+ @NonNull Map<String, Set<String>> namespaceMap,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
ResultSpecProto.Builder resultSpecBuilder = ResultSpecProto.newBuilder()
.setNumPerPage(mSearchSpec.getResultCountPerPage())
.setSnippetSpec(
@@ -263,6 +348,11 @@
.setNumToSnippet(mSearchSpec.getSnippetCount())
.setNumMatchesPerProperty(mSearchSpec.getSnippetCountPerProperty())
.setMaxWindowUtf32Length(mSearchSpec.getMaxSnippetSize()));
+ JoinSpec joinSpec = mSearchSpec.getJoinSpec();
+ if (joinSpec != null) {
+ resultSpecBuilder.setMaxJoinedChildrenPerParentToReturn(
+ joinSpec.getMaxJoinedResultCount());
+ }
// Rewrites the typePropertyMasks that exist in {@code prefixes}.
int groupingType = mSearchSpec.getResultGroupingTypeFlags();
@@ -279,12 +369,36 @@
namespaceMap, resultSpecBuilder);
resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE;
break;
+ case SearchSpec.GROUPING_TYPE_PER_SCHEMA:
+ addPerSchemaResultGrouping(mPrefixes, mSearchSpec.getResultGroupingLimit(),
+ schemaMap, resultSpecBuilder);
+ resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
+ break;
case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE:
addPerPackagePerNamespaceResultGroupings(mPrefixes,
mSearchSpec.getResultGroupingLimit(),
namespaceMap, resultSpecBuilder);
resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE;
break;
+ case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
+ addPerPackagePerSchemaResultGroupings(mPrefixes,
+ mSearchSpec.getResultGroupingLimit(),
+ schemaMap, resultSpecBuilder);
+ resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
+ break;
+ case SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
+ addPerNamespaceAndSchemaResultGrouping(mPrefixes,
+ mSearchSpec.getResultGroupingLimit(),
+ namespaceMap, schemaMap, resultSpecBuilder);
+ resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
+ break;
+ case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
+ addPerPackagePerNamespacePerSchemaResultGrouping(mPrefixes,
+ mSearchSpec.getResultGroupingLimit(),
+ namespaceMap, schemaMap, resultSpecBuilder);
+ resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
+ break;
default:
break;
}
@@ -363,21 +477,54 @@
}
/**
- * Adds result groupings for each namespace in each package being queried for.
+ * Returns a Map of namespace to prefixedNamespaces. This is NOT necessarily the
+ * same as the list of namespaces. If a namespace exists under different packages and/or
+ * different databases, they should still be grouped together.
*
- * @param prefixes Prefixes that we should prepend to all our filters
- * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param prefixes Prefixes that we should prepend to all our filters.
* @param namespaceMap The namespace map contains all prefixed existing namespaces.
- * @param resultSpecBuilder ResultSpecs as specified by client
*/
- private static void addPerPackagePerNamespaceResultGroupings(
+ private static Map<String, List<String>> getNamespaceToPrefixedNamespaces(
@NonNull Set<String> prefixes,
- int maxNumResults,
- @NonNull Map<String, Set<String>> namespaceMap,
- @NonNull ResultSpecProto.Builder resultSpecBuilder) {
- // Create a map for package+namespace to prefixedNamespaces. This is NOT necessarily the
- // same as the list of namespaces. If one package has multiple databases, each with the same
- // namespace, then those should be grouped together.
+ @NonNull Map<String, Set<String>> namespaceMap) {
+ Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
+ for (String prefix : prefixes) {
+ Set<String> prefixedNamespaces = namespaceMap.get(prefix);
+ if (prefixedNamespaces == null) {
+ continue;
+ }
+ for (String prefixedNamespace : prefixedNamespaces) {
+ String namespace;
+ try {
+ namespace = removePrefix(prefixedNamespace);
+ } catch (AppSearchException e) {
+ // This should never happen. Skip this namespace if it does.
+ Log.e(TAG, "Prefixed namespace " + prefixedNamespace + " is malformed.");
+ continue;
+ }
+ List<String> groupedPrefixedNamespaces =
+ namespaceToPrefixedNamespaces.get(namespace);
+ if (groupedPrefixedNamespaces == null) {
+ groupedPrefixedNamespaces = new ArrayList<>();
+ namespaceToPrefixedNamespaces.put(namespace, groupedPrefixedNamespaces);
+ }
+ groupedPrefixedNamespaces.add(prefixedNamespace);
+ }
+ }
+ return namespaceToPrefixedNamespaces;
+ }
+
+ /**
+ * Returns a map for package+namespace to prefixedNamespaces. This is NOT necessarily the
+ * same as the list of namespaces. If one package has multiple databases, each with the same
+ * namespace, then those should be grouped together.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param namespaceMap The namespace map contains all prefixed existing namespaces.
+ */
+ private static Map<String, List<String>> getPackageAndNamespaceToPrefixedNamespaces(
+ @NonNull Set<String> prefixes,
+ @NonNull Map<String, Set<String>> namespaceMap) {
Map<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>();
for (String prefix : prefixes) {
Set<String> prefixedNamespaces = namespaceMap.get(prefix);
@@ -408,14 +555,113 @@
namespaceList.add(prefixedNamespace);
}
}
+ return packageAndNamespaceToNamespaces;
+ }
+
+ /**
+ * Returns a map of schema to prefixedSchemas. This is NOT necessarily the
+ * same as the list of schemas. If a schema exists under different packages and/or
+ * different databases, they should still be grouped together.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ */
+ private static Map<String, List<String>> getSchemaToPrefixedSchemas(
+ @NonNull Set<String> prefixes,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+ Map<String, List<String>> schemaToPrefixedSchemas = new ArrayMap<>();
+ for (String prefix : prefixes) {
+ Map<String, SchemaTypeConfigProto> prefixedSchemas = schemaMap.get(prefix);
+ if (prefixedSchemas == null) {
+ continue;
+ }
+ for (String prefixedSchema : prefixedSchemas.keySet()) {
+ String schema;
+ try {
+ schema = removePrefix(prefixedSchema);
+ } catch (AppSearchException e) {
+ // This should never happen. Skip this schema if it does.
+ Log.e(TAG, "Prefixed schema " + prefixedSchema + " is malformed.");
+ continue;
+ }
+ List<String> groupedPrefixedSchemas =
+ schemaToPrefixedSchemas.get(schema);
+ if (groupedPrefixedSchemas == null) {
+ groupedPrefixedSchemas = new ArrayList<>();
+ schemaToPrefixedSchemas.put(schema, groupedPrefixedSchemas);
+ }
+ groupedPrefixedSchemas.add(prefixedSchema);
+ }
+ }
+ return schemaToPrefixedSchemas;
+ }
+
+ /**
+ * Returns a map for package+schema to prefixedSchemas. This is NOT necessarily the
+ * same as the list of schemas. If one package has multiple databases, each with the same
+ * schema, then those should be grouped together.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ */
+ private static Map<String, List<String>> getPackageAndSchemaToPrefixedSchemas(
+ @NonNull Set<String> prefixes,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+ Map<String, List<String>> packageAndSchemaToSchemas = new ArrayMap<>();
+ for (String prefix : prefixes) {
+ Map<String, SchemaTypeConfigProto> prefixedSchemas = schemaMap.get(prefix);
+ if (prefixedSchemas == null) {
+ continue;
+ }
+ String packageName = getPackageName(prefix);
+ // Create a new prefix without the database name. This will allow us to group schemas
+ // that have the same name and package but a different database name together.
+ String emptyDatabasePrefix = createPrefix(packageName, /*database*/"");
+ for (String prefixedSchema : prefixedSchemas.keySet()) {
+ String schema;
+ try {
+ schema = removePrefix(prefixedSchema);
+ } catch (AppSearchException e) {
+ // This should never happen. Skip this schema if it does.
+ Log.e(TAG, "Prefixed schema " + prefixedSchema + " is malformed.");
+ continue;
+ }
+ String emptyDatabasePrefixedSchema = emptyDatabasePrefix + schema;
+ List<String> schemaList =
+ packageAndSchemaToSchemas.get(emptyDatabasePrefixedSchema);
+ if (schemaList == null) {
+ schemaList = new ArrayList<>();
+ packageAndSchemaToSchemas.put(emptyDatabasePrefixedSchema, schemaList);
+ }
+ schemaList.add(prefixedSchema);
+ }
+ }
+ return packageAndSchemaToSchemas;
+ }
+
+ /**
+ * Adds result groupings for each namespace in each package being queried for.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters
+ * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param namespaceMap The namespace map contains all prefixed existing namespaces.
+ * @param resultSpecBuilder ResultSpecs as specified by client
+ */
+ private static void addPerPackagePerNamespaceResultGroupings(
+ @NonNull Set<String> prefixes,
+ int maxNumResults,
+ @NonNull Map<String, Set<String>> namespaceMap,
+ @NonNull ResultSpecProto.Builder resultSpecBuilder) {
+ Map<String, List<String>> packageAndNamespaceToNamespaces =
+ getPackageAndNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
for (List<String> prefixedNamespaces : packageAndNamespaceToNamespaces.values()) {
List<ResultSpecProto.ResultGrouping.Entry> entries =
new ArrayList<>(prefixedNamespaces.size());
- for (String namespace : prefixedNamespaces) {
+ for (int i = 0; i < prefixedNamespaces.size(); i++) {
entries.add(
ResultSpecProto.ResultGrouping.Entry.newBuilder()
- .setNamespace(namespace).build());
+ .setNamespace(prefixedNamespaces.get(i)).build());
}
resultSpecBuilder.addResultGroupings(
ResultSpecProto.ResultGrouping.newBuilder()
@@ -424,6 +670,84 @@
}
/**
+ * Adds result groupings for each schema type in each package being queried for.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ * @param resultSpecBuilder ResultSpecs as a specified by client.
+ */
+ private static void addPerPackagePerSchemaResultGroupings(
+ @NonNull Set<String> prefixes,
+ int maxNumResults,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+ @NonNull ResultSpecProto.Builder resultSpecBuilder) {
+ Map<String, List<String>> packageAndSchemaToSchemas =
+ getPackageAndSchemaToPrefixedSchemas(prefixes, schemaMap);
+
+ for (List<String> prefixedSchemas : packageAndSchemaToSchemas.values()) {
+ List<ResultSpecProto.ResultGrouping.Entry> entries =
+ new ArrayList<>(prefixedSchemas.size());
+ for (int i = 0; i < prefixedSchemas.size(); i++) {
+ entries.add(
+ ResultSpecProto.ResultGrouping.Entry.newBuilder()
+ .setSchema(prefixedSchemas.get(i)).build());
+ }
+ resultSpecBuilder.addResultGroupings(
+ ResultSpecProto.ResultGrouping.newBuilder()
+ .addAllEntryGroupings(entries).setMaxResults(maxNumResults));
+ }
+ }
+
+ /**
+ * Adds result groupings for each namespace and schema type being queried for.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param namespaceMap The namespace map contains all prefixed existing namespaces.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ * @param resultSpecBuilder ResultSpec as specified by client.
+ */
+ private static void addPerPackagePerNamespacePerSchemaResultGrouping(
+ @NonNull Set<String> prefixes,
+ int maxNumResults,
+ @NonNull Map<String, Set<String>> namespaceMap,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+ @NonNull ResultSpecProto.Builder resultSpecBuilder) {
+ Map<String, List<String>> packageAndNamespaceToNamespaces =
+ getPackageAndNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
+ Map<String, List<String>> packageAndSchemaToSchemas =
+ getPackageAndSchemaToPrefixedSchemas(prefixes, schemaMap);
+
+ for (List<String> prefixedNamespaces : packageAndNamespaceToNamespaces.values()) {
+ for (List<String> prefixedSchemas : packageAndSchemaToSchemas.values()) {
+ List<ResultSpecProto.ResultGrouping.Entry> entries =
+ new ArrayList<>(prefixedNamespaces.size() * prefixedSchemas.size());
+ // Iterate through all namespaces.
+ for (int i = 0; i < prefixedNamespaces.size(); i++) {
+ String namespacePackage = getPackageName(prefixedNamespaces.get(i));
+ // Iterate through all schemas.
+ for (int j = 0; j < prefixedSchemas.size(); j++) {
+ String schemaPackage = getPackageName(prefixedSchemas.get(j));
+ if (namespacePackage.equals(schemaPackage)) {
+ entries.add(
+ ResultSpecProto.ResultGrouping.Entry.newBuilder()
+ .setNamespace(prefixedNamespaces.get(i))
+ .setSchema(prefixedSchemas.get(j))
+ .build());
+ }
+ }
+ }
+ if (entries.size() > 0) {
+ resultSpecBuilder.addResultGroupings(
+ ResultSpecProto.ResultGrouping.newBuilder()
+ .addAllEntryGroupings(entries).setMaxResults(maxNumResults));
+ }
+ }
+ }
+ }
+
+ /**
* Adds result groupings for each package being queried for.
*
* @param prefixes Prefixes that we should prepend to all our filters
@@ -479,42 +803,16 @@
int maxNumResults,
@NonNull Map<String, Set<String>> namespaceMap,
@NonNull ResultSpecProto.Builder resultSpecBuilder) {
- // Create a map of namespace to prefixedNamespaces. This is NOT necessarily the
- // same as the list of namespaces. If a namespace exists under different packages and/or
- // different databases, they should still be grouped together.
- Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
- for (String prefix : prefixes) {
- Set<String> prefixedNamespaces = namespaceMap.get(prefix);
- if (prefixedNamespaces == null) {
- continue;
- }
- for (String prefixedNamespace : prefixedNamespaces) {
- String namespace;
- try {
- namespace = removePrefix(prefixedNamespace);
- } catch (AppSearchException e) {
- // This should never happen. Skip this namespace if it does.
- Log.e(TAG, "Prefixed namespace " + prefixedNamespace + " is malformed.");
- continue;
- }
- List<String> groupedPrefixedNamespaces =
- namespaceToPrefixedNamespaces.get(namespace);
- if (groupedPrefixedNamespaces == null) {
- groupedPrefixedNamespaces = new ArrayList<>();
- namespaceToPrefixedNamespaces.put(namespace,
- groupedPrefixedNamespaces);
- }
- groupedPrefixedNamespaces.add(prefixedNamespace);
- }
- }
+ Map<String, List<String>> namespaceToPrefixedNamespaces =
+ getNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
for (List<String> prefixedNamespaces : namespaceToPrefixedNamespaces.values()) {
List<ResultSpecProto.ResultGrouping.Entry> entries =
new ArrayList<>(prefixedNamespaces.size());
- for (String namespace : prefixedNamespaces) {
+ for (int i = 0; i < prefixedNamespaces.size(); i++) {
entries.add(
ResultSpecProto.ResultGrouping.Entry.newBuilder()
- .setNamespace(namespace).build());
+ .setNamespace(prefixedNamespaces.get(i)).build());
}
resultSpecBuilder.addResultGroupings(
ResultSpecProto.ResultGrouping.newBuilder()
@@ -523,6 +821,90 @@
}
/**
+ * Adds result groupings for each schema type being queried for.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ * @param resultSpecBuilder ResultSpec as specified by client.
+ */
+ private static void addPerSchemaResultGrouping(
+ @NonNull Set<String> prefixes,
+ int maxNumResults,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+ @NonNull ResultSpecProto.Builder resultSpecBuilder) {
+ Map<String, List<String>> schemaToPrefixedSchemas =
+ getSchemaToPrefixedSchemas(prefixes, schemaMap);
+
+ for (List<String> prefixedSchemas : schemaToPrefixedSchemas.values()) {
+ List<ResultSpecProto.ResultGrouping.Entry> entries =
+ new ArrayList<>(prefixedSchemas.size());
+ for (int i = 0; i < prefixedSchemas.size(); i++) {
+ entries.add(
+ ResultSpecProto.ResultGrouping.Entry.newBuilder()
+ .setSchema(prefixedSchemas.get(i)).build());
+ }
+ resultSpecBuilder.addResultGroupings(
+ ResultSpecProto.ResultGrouping.newBuilder()
+ .addAllEntryGroupings(entries).setMaxResults(maxNumResults));
+ }
+ }
+
+ /**
+ * Adds result groupings for each namespace and schema type being queried for.
+ *
+ * @param prefixes Prefixes that we should prepend to all our filters.
+ * @param maxNumResults The maximum number of results for each grouping to support.
+ * @param namespaceMap The namespace map contains all prefixed existing namespaces.
+ * @param schemaMap The schema map contains all prefixed existing schema types.
+ * @param resultSpecBuilder ResultSpec as specified by client.
+ */
+ private static void addPerNamespaceAndSchemaResultGrouping(
+ @NonNull Set<String> prefixes,
+ int maxNumResults,
+ @NonNull Map<String, Set<String>> namespaceMap,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+ @NonNull ResultSpecProto.Builder resultSpecBuilder) {
+ Map<String, List<String>> namespaceToPrefixedNamespaces =
+ getNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
+ Map<String, List<String>> schemaToPrefixedSchemas =
+ getSchemaToPrefixedSchemas(prefixes, schemaMap);
+
+ for (List<String> prefixedNamespaces : namespaceToPrefixedNamespaces.values()) {
+ for (List<String> prefixedSchemas : schemaToPrefixedSchemas.values()) {
+ List<ResultSpecProto.ResultGrouping.Entry> entries =
+ new ArrayList<>(prefixedNamespaces.size() * prefixedSchemas.size());
+ // Iterate through all namespaces.
+ for (int i = 0; i < prefixedNamespaces.size(); i++) {
+ // Iterate through all schemas.
+ for (int j = 0; j < prefixedSchemas.size(); j++) {
+ try {
+ if (getPrefix(prefixedNamespaces.get(i))
+ .equals(getPrefix(prefixedSchemas.get(j)))) {
+ entries.add(
+ ResultSpecProto.ResultGrouping.Entry.newBuilder()
+ .setNamespace(prefixedNamespaces.get(i))
+ .setSchema(prefixedSchemas.get(j))
+ .build());
+ }
+ } catch (AppSearchException e) {
+ // This should never happen. Skip this schema if it does.
+ Log.e(TAG, "Prefixed string " + prefixedNamespaces.get(i) + " or "
+ + prefixedSchemas.get(j) + " is malformed.");
+ continue;
+ }
+ }
+ }
+ if (entries.size() > 0) {
+ resultSpecBuilder.addResultGroupings(
+ ResultSpecProto.ResultGrouping.newBuilder()
+ .addAllEntryGroupings(entries).setMaxResults(maxNumResults));
+ }
+ }
+ }
+ }
+
+ /**
* Adds {@link TypePropertyWeights} to {@link ScoringSpecProto}.
*
* <p>{@link TypePropertyWeights} are added to the {@link ScoringSpecProto} with database and
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
index cb29317..02d2971 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
@@ -20,11 +20,16 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
+import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Set;
/**
* A class for setting basic information to log for all function calls.
@@ -36,7 +41,7 @@
* However, {@link CallStats} can still be used along with the detailed stats class for easy
* aggregation/analysis with other function calls.
*
- * @hide
+ * <!--@exportToFramework:hide-->
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class CallStats {
@@ -58,6 +63,20 @@
CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH,
CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID,
CALL_TYPE_SCHEMA_MIGRATION,
+ CALL_TYPE_GLOBAL_GET_SCHEMA,
+ CALL_TYPE_GET_SCHEMA,
+ CALL_TYPE_GET_NAMESPACES,
+ CALL_TYPE_GET_NEXT_PAGE,
+ CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN,
+ CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE,
+ CALL_TYPE_PUT_DOCUMENTS_FROM_FILE,
+ CALL_TYPE_SEARCH_SUGGESTION,
+ CALL_TYPE_REPORT_SYSTEM_USAGE,
+ CALL_TYPE_REPORT_USAGE,
+ CALL_TYPE_GET_STORAGE_INFO,
+ CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
+ CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
+ CALL_TYPE_GLOBAL_GET_NEXT_PAGE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CallType {
@@ -80,6 +99,51 @@
public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14;
public static final int CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID = 15;
public static final int CALL_TYPE_SCHEMA_MIGRATION = 16;
+ public static final int CALL_TYPE_GLOBAL_GET_SCHEMA = 17;
+ public static final int CALL_TYPE_GET_SCHEMA = 18;
+ public static final int CALL_TYPE_GET_NAMESPACES = 19;
+ public static final int CALL_TYPE_GET_NEXT_PAGE = 20;
+ public static final int CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN = 21;
+ public static final int CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE = 22;
+ public static final int CALL_TYPE_PUT_DOCUMENTS_FROM_FILE = 23;
+ public static final int CALL_TYPE_SEARCH_SUGGESTION = 24;
+ public static final int CALL_TYPE_REPORT_SYSTEM_USAGE = 25;
+ public static final int CALL_TYPE_REPORT_USAGE = 26;
+ public static final int CALL_TYPE_GET_STORAGE_INFO = 27;
+ public static final int CALL_TYPE_REGISTER_OBSERVER_CALLBACK = 28;
+ public static final int CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK = 29;
+ public static final int CALL_TYPE_GLOBAL_GET_NEXT_PAGE = 30;
+
+ // These strings are for the subset of call types that correspond to an AppSearchManager API
+ private static final String CALL_TYPE_STRING_INITIALIZE = "initialize";
+ private static final String CALL_TYPE_STRING_SET_SCHEMA = "localSetSchema";
+ private static final String CALL_TYPE_STRING_PUT_DOCUMENTS = "localPutDocuments";
+ private static final String CALL_TYPE_STRING_GET_DOCUMENTS = "localGetDocuments";
+ private static final String CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_ID = "localRemoveByDocumentId";
+ private static final String CALL_TYPE_STRING_SEARCH = "localSearch";
+ private static final String CALL_TYPE_STRING_FLUSH = "flush";
+ private static final String CALL_TYPE_STRING_GLOBAL_SEARCH = "globalSearch";
+ private static final String CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_SEARCH = "localRemoveBySearch";
+ private static final String CALL_TYPE_STRING_GLOBAL_GET_DOCUMENT_BY_ID = "globalGetDocuments";
+ private static final String CALL_TYPE_STRING_GLOBAL_GET_SCHEMA = "globalGetSchema";
+ private static final String CALL_TYPE_STRING_GET_SCHEMA = "localGetSchema";
+ private static final String CALL_TYPE_STRING_GET_NAMESPACES = "localGetNamespaces";
+ private static final String CALL_TYPE_STRING_GET_NEXT_PAGE = "localGetNextPage";
+ private static final String CALL_TYPE_STRING_INVALIDATE_NEXT_PAGE_TOKEN =
+ "invalidateNextPageToken";
+ private static final String CALL_TYPE_STRING_WRITE_SEARCH_RESULTS_TO_FILE =
+ "localWriteSearchResultsToFile";
+ private static final String CALL_TYPE_STRING_PUT_DOCUMENTS_FROM_FILE =
+ "localPutDocumentsFromFile";
+ private static final String CALL_TYPE_STRING_SEARCH_SUGGESTION = "localSearchSuggestion";
+ private static final String CALL_TYPE_STRING_REPORT_SYSTEM_USAGE = "globalReportUsage";
+ private static final String CALL_TYPE_STRING_REPORT_USAGE = "localReportUsage";
+ private static final String CALL_TYPE_STRING_GET_STORAGE_INFO = "localGetStorageInfo";
+ private static final String CALL_TYPE_STRING_REGISTER_OBSERVER_CALLBACK =
+ "globalRegisterObserverCallback";
+ private static final String CALL_TYPE_STRING_UNREGISTER_OBSERVER_CALLBACK =
+ "globalUnregisterObserverCallback";
+ private static final String CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE = "globalGetNextPage";
@Nullable
private final String mPackageName;
@@ -149,8 +213,9 @@
* Returns number of operations succeeded.
*
* <p>For example, for
- * {@link androidx.appsearch.app.AppSearchSession#putAsync}, it is the total number of individual
- * successful put operations. In this case, how many documents are successfully indexed.
+ * {@link androidx.appsearch.app.AppSearchSession#putAsync}, it is the total number of
+ * individual successful put operations. In this case, how many documents are successfully
+ * indexed.
*
* <p>For non-batch calls such as
* {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync}, the sum of
@@ -166,8 +231,8 @@
* Returns number of operations failed.
*
* <p>For example, for
- * {@link androidx.appsearch.app.AppSearchSession#putAsync}, it is the total number of individual
- * failed put operations. In this case, how many documents are failed to be indexed.
+ * {@link androidx.appsearch.app.AppSearchSession#putAsync}, it is the total number of
+ * individual failed put operations. In this case, how many documents are failed to be indexed.
*
* <p>For non-batch calls such as
* {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync}, the sum of
@@ -195,20 +260,23 @@
int mNumOperationsFailed;
/** Sets the PackageName used by the session. */
+ @CanIgnoreReturnValue
@NonNull
- public Builder setPackageName(@NonNull String packageName) {
- mPackageName = Preconditions.checkNotNull(packageName);
+ public Builder setPackageName(@Nullable String packageName) {
+ mPackageName = packageName;
return this;
}
/** Sets the database used by the session. */
+ @CanIgnoreReturnValue
@NonNull
- public Builder setDatabase(@NonNull String database) {
- mDatabase = Preconditions.checkNotNull(database);
+ public Builder setDatabase(@Nullable String database) {
+ mDatabase = database;
return this;
}
/** Sets the status code. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -216,6 +284,7 @@
}
/** Sets total latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -223,6 +292,7 @@
}
/** Sets type of the call. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setCallType(@CallType int callType) {
mCallType = callType;
@@ -230,6 +300,7 @@
}
/** Sets estimated binder latency, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis) {
mEstimatedBinderLatencyMillis = estimatedBinderLatencyMillis;
@@ -250,6 +321,7 @@
* {@link CallStats#getNumOperationsFailed()} is always 1 since there is only one
* operation.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setNumOperationsSucceeded(int numOperationsSucceeded) {
mNumOperationsSucceeded = numOperationsSucceeded;
@@ -269,6 +341,7 @@
* {@link CallStats#getNumOperationsFailed()} is always 1 since there is only one
* operation.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setNumOperationsFailed(int numOperationsFailed) {
mNumOperationsFailed = numOperationsFailed;
@@ -281,4 +354,97 @@
return new CallStats(/* builder= */ this);
}
}
+
+ /**
+ * Returns the {@link CallStats.CallType} represented by the given AppSearchManager API name. If
+ * an unknown name is provided, {@link CallStats.CallType#CALL_TYPE_UNKNOWN} is returned.
+ */
+ @CallType
+ public static int getApiCallTypeFromName(@NonNull String name) {
+ switch (name) {
+ case CALL_TYPE_STRING_INITIALIZE:
+ return CALL_TYPE_INITIALIZE;
+ case CALL_TYPE_STRING_SET_SCHEMA:
+ return CALL_TYPE_SET_SCHEMA;
+ case CALL_TYPE_STRING_PUT_DOCUMENTS:
+ return CALL_TYPE_PUT_DOCUMENTS;
+ case CALL_TYPE_STRING_GET_DOCUMENTS:
+ return CALL_TYPE_GET_DOCUMENTS;
+ case CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_ID:
+ return CALL_TYPE_REMOVE_DOCUMENTS_BY_ID;
+ case CALL_TYPE_STRING_SEARCH:
+ return CALL_TYPE_SEARCH;
+ case CALL_TYPE_STRING_FLUSH:
+ return CALL_TYPE_FLUSH;
+ case CALL_TYPE_STRING_GLOBAL_SEARCH:
+ return CALL_TYPE_GLOBAL_SEARCH;
+ case CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_SEARCH:
+ return CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH;
+ case CALL_TYPE_STRING_GLOBAL_GET_DOCUMENT_BY_ID:
+ return CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID;
+ case CALL_TYPE_STRING_GLOBAL_GET_SCHEMA:
+ return CALL_TYPE_GLOBAL_GET_SCHEMA;
+ case CALL_TYPE_STRING_GET_SCHEMA:
+ return CALL_TYPE_GET_SCHEMA;
+ case CALL_TYPE_STRING_GET_NAMESPACES:
+ return CALL_TYPE_GET_NAMESPACES;
+ case CALL_TYPE_STRING_GET_NEXT_PAGE:
+ return CALL_TYPE_GET_NEXT_PAGE;
+ case CALL_TYPE_STRING_INVALIDATE_NEXT_PAGE_TOKEN:
+ return CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN;
+ case CALL_TYPE_STRING_WRITE_SEARCH_RESULTS_TO_FILE:
+ return CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE;
+ case CALL_TYPE_STRING_PUT_DOCUMENTS_FROM_FILE:
+ return CALL_TYPE_PUT_DOCUMENTS_FROM_FILE;
+ case CALL_TYPE_STRING_SEARCH_SUGGESTION:
+ return CALL_TYPE_SEARCH_SUGGESTION;
+ case CALL_TYPE_STRING_REPORT_SYSTEM_USAGE:
+ return CALL_TYPE_REPORT_SYSTEM_USAGE;
+ case CALL_TYPE_STRING_REPORT_USAGE:
+ return CALL_TYPE_REPORT_USAGE;
+ case CALL_TYPE_STRING_GET_STORAGE_INFO:
+ return CALL_TYPE_GET_STORAGE_INFO;
+ case CALL_TYPE_STRING_REGISTER_OBSERVER_CALLBACK:
+ return CALL_TYPE_REGISTER_OBSERVER_CALLBACK;
+ case CALL_TYPE_STRING_UNREGISTER_OBSERVER_CALLBACK:
+ return CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK;
+ case CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE:
+ return CALL_TYPE_GLOBAL_GET_NEXT_PAGE;
+ default:
+ return CALL_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Returns the set of all {@link CallStats.CallType} that map to an AppSearchManager API.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static Set<Integer> getAllApiCallTypes() {
+ return new ArraySet<>(Arrays.asList(
+ CALL_TYPE_INITIALIZE,
+ CALL_TYPE_SET_SCHEMA,
+ CALL_TYPE_PUT_DOCUMENTS,
+ CALL_TYPE_GET_DOCUMENTS,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
+ CALL_TYPE_SEARCH,
+ CALL_TYPE_FLUSH,
+ CALL_TYPE_GLOBAL_SEARCH,
+ CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
+ CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID,
+ CALL_TYPE_GLOBAL_GET_SCHEMA,
+ CALL_TYPE_GET_SCHEMA,
+ CALL_TYPE_GET_NAMESPACES,
+ CALL_TYPE_GET_NEXT_PAGE,
+ CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN,
+ CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE,
+ CALL_TYPE_PUT_DOCUMENTS_FROM_FILE,
+ CALL_TYPE_SEARCH_SUGGESTION,
+ CALL_TYPE_REPORT_SYSTEM_USAGE,
+ CALL_TYPE_REPORT_USAGE,
+ CALL_TYPE_GET_STORAGE_INFO,
+ CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
+ CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
+ CALL_TYPE_GLOBAL_GET_NEXT_PAGE));
+ }
}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/InitializeStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/InitializeStats.java
index f88d257..6effe35 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/InitializeStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/InitializeStats.java
@@ -19,6 +19,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.core.util.Preconditions;
@@ -288,6 +289,7 @@
int mResetStatusCode;
/** Sets the status of the initialization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -295,6 +297,7 @@
}
/** Sets the total latency of the initialization in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -307,6 +310,7 @@
* <p>If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent
* view of what data should exist.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setHasDeSync(boolean hasDeSync) {
mHasDeSync = hasDeSync;
@@ -314,6 +318,7 @@
}
/** Sets time used to read and process the schema and namespaces. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setPrepareSchemaAndNamespacesLatencyMillis(
int prepareSchemaAndNamespacesLatencyMillis) {
@@ -322,6 +327,7 @@
}
/** Sets time used to read and process the visibility file. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setPrepareVisibilityStoreLatencyMillis(
int prepareVisibilityStoreLatencyMillis) {
@@ -330,6 +336,7 @@
}
/** Sets overall time used for the native function call. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
mNativeLatencyMillis = nativeLatencyMillis;
@@ -344,6 +351,7 @@
* <li> {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
* <li> {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentStoreRecoveryCause(
@RecoveryCause int documentStoreRecoveryCause) {
@@ -358,6 +366,7 @@
* <li> {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
* <li> {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setIndexRestorationCause(
@RecoveryCause int indexRestorationCause) {
@@ -370,6 +379,7 @@
* <p> Possible causes:
* <li> {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setSchemaStoreRecoveryCause(
@RecoveryCause int schemaStoreRecoveryCause) {
@@ -378,6 +388,7 @@
}
/** Sets time used to recover the document store. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentStoreRecoveryLatencyMillis(
int documentStoreRecoveryLatencyMillis) {
@@ -386,6 +397,7 @@
}
/** Sets time used to restore the index. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setIndexRestorationLatencyMillis(
int indexRestorationLatencyMillis) {
@@ -394,6 +406,7 @@
}
/** Sets time used to recover the schema store. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSchemaStoreRecoveryLatencyMillis(
int schemaStoreRecoveryLatencyMillis) {
@@ -405,6 +418,7 @@
* Sets Native Document Store Data status.
* status is defined in external/icing/proto/icing/proto/logging.proto
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentStoreDataStatus(
@DocumentStoreDataStatus int documentStoreDataStatus) {
@@ -416,6 +430,7 @@
* Sets number of documents currently in document store. Those may include alive, deleted,
* and expired documents.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentCount(int numDocuments) {
mNativeNumDocuments = numDocuments;
@@ -423,6 +438,7 @@
}
/** Sets number of schema types currently in the schema store. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSchemaTypeCount(int numSchemaTypes) {
mNativeNumSchemaTypes = numSchemaTypes;
@@ -430,6 +446,7 @@
}
/** Sets whether we had to reset the index, losing all data, as part of initialization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setHasReset(boolean hasReset) {
mHasReset = hasReset;
@@ -437,6 +454,7 @@
}
/** Sets the status of the reset, if one was performed according to {@link #setHasReset}. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setResetStatusCode(@AppSearchResult.ResultCode int resetStatusCode) {
mResetStatusCode = resetStatusCode;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/OptimizeStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/OptimizeStats.java
index b7dcae0..2a47183 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/OptimizeStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/OptimizeStats.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.core.util.Preconditions;
@@ -156,6 +157,7 @@
long mNativeTimeSinceLastOptimizeMillis;
/** Sets the status code. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -163,6 +165,7 @@
}
/** Sets total latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -170,6 +173,7 @@
}
/** Sets native latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
mNativeLatencyMillis = nativeLatencyMillis;
@@ -177,6 +181,7 @@
}
/** Sets time used to optimize the document store. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentStoreOptimizeLatencyMillis(
int documentStoreOptimizeLatencyMillis) {
@@ -185,6 +190,7 @@
}
/** Sets time used to restore the index. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) {
mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis;
@@ -192,6 +198,7 @@
}
/** Sets number of documents before the optimization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setOriginalDocumentCount(int originalDocumentCount) {
mNativeOriginalDocumentCount = originalDocumentCount;
@@ -199,6 +206,7 @@
}
/** Sets number of documents deleted during the optimization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDeletedDocumentCount(int deletedDocumentCount) {
mNativeDeletedDocumentCount = deletedDocumentCount;
@@ -206,6 +214,7 @@
}
/** Sets number of documents expired during the optimization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setExpiredDocumentCount(int expiredDocumentCount) {
mNativeExpiredDocumentCount = expiredDocumentCount;
@@ -213,6 +222,7 @@
}
/** Sets Storage size in bytes before optimization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStorageSizeBeforeBytes(long storageSizeBeforeBytes) {
mNativeStorageSizeBeforeBytes = storageSizeBeforeBytes;
@@ -220,6 +230,7 @@
}
/** Sets storage size in bytes after optimization. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStorageSizeAfterBytes(long storageSizeAfterBytes) {
mNativeStorageSizeAfterBytes = storageSizeAfterBytes;
@@ -229,6 +240,7 @@
/**
* Sets the amount the time since the last optimize ran calculated using wall clock time.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setTimeSinceLastOptimizeMillis(long timeSinceLastOptimizeMillis) {
mNativeTimeSinceLastOptimizeMillis = timeSinceLastOptimizeMillis;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/PutDocumentStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/PutDocumentStats.java
index e9a25fd..3378df7 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/PutDocumentStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/PutDocumentStats.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.core.util.Preconditions;
@@ -169,6 +170,7 @@
}
/** Sets the status code. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -176,6 +178,7 @@
}
/** Sets total latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -183,6 +186,7 @@
}
/** Sets how much time we spend for generating document proto, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setGenerateDocumentProtoLatencyMillis(
int generateDocumentProtoLatencyMillis) {
@@ -194,6 +198,7 @@
* Sets how much time we spend for rewriting types and namespaces in document, in
* milliseconds.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setRewriteDocumentTypesLatencyMillis(int rewriteDocumentTypesLatencyMillis) {
mRewriteDocumentTypesLatencyMillis = rewriteDocumentTypesLatencyMillis;
@@ -201,6 +206,7 @@
}
/** Sets the native latency, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
mNativeLatencyMillis = nativeLatencyMillis;
@@ -208,6 +214,7 @@
}
/** Sets how much time we spend on document store, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeDocumentStoreLatencyMillis(int nativeDocumentStoreLatencyMillis) {
mNativeDocumentStoreLatencyMillis = nativeDocumentStoreLatencyMillis;
@@ -215,6 +222,7 @@
}
/** Sets the native index latency, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeIndexLatencyMillis(int nativeIndexLatencyMillis) {
mNativeIndexLatencyMillis = nativeIndexLatencyMillis;
@@ -222,6 +230,7 @@
}
/** Sets how much time we spend on merging indices, in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeIndexMergeLatencyMillis(int nativeIndexMergeLatencyMillis) {
mNativeIndexMergeLatencyMillis = nativeIndexMergeLatencyMillis;
@@ -229,6 +238,7 @@
}
/** Sets document size, in bytes. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeDocumentSizeBytes(int nativeDocumentSizeBytes) {
mNativeDocumentSizeBytes = nativeDocumentSizeBytes;
@@ -236,6 +246,7 @@
}
/** Sets number of tokens indexed in native. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeNumTokensIndexed(int nativeNumTokensIndexed) {
mNativeNumTokensIndexed = nativeNumTokensIndexed;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
index 7eb4820..ffb3f3a 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
@@ -19,6 +19,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.appsearch.app.RemoveByDocumentIdRequest;
import androidx.appsearch.app.SearchSpec;
@@ -148,6 +149,7 @@
}
/** Sets the status code. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -155,6 +157,7 @@
}
/** Sets total latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -162,6 +165,7 @@
}
/** Sets native latency in millis. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
mNativeLatencyMillis = nativeLatencyMillis;
@@ -169,6 +173,7 @@
}
/** Sets delete type for this call. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDeleteType(@DeleteType int nativeDeleteType) {
mNativeDeleteType = nativeDeleteType;
@@ -176,6 +181,7 @@
}
/** Sets how many documents get deleted for this call. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDeletedDocumentCount(int nativeNumDocumentsDeleted) {
mNativeNumDocumentsDeleted = nativeNumDocumentsDeleted;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
index bc46326..6945221 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
@@ -19,7 +19,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JoinableValueType;
import androidx.appsearch.app.SearchSpec;
import androidx.core.util.Preconditions;
@@ -129,7 +131,12 @@
private final int mJavaToNativeJniLatencyMillis;
/** Time used to send data across the JNI boundary from native to java side. */
private final int mNativeToJavaJniLatencyMillis;
-
+ /** The type of join performed. Zero if no join is performed */
+ @JoinableValueType private final int mJoinType;
+ /** The total number of joined documents in the current page. */
+ private final int mNativeNumJoinedResultsCurrentPage;
+ /** Time taken to join documents together. */
+ private final int mNativeJoinLatencyMillis;
SearchStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder);
@@ -160,6 +167,9 @@
mNativeLockAcquisitionLatencyMillis = builder.mNativeLockAcquisitionLatencyMillis;
mJavaToNativeJniLatencyMillis = builder.mJavaToNativeJniLatencyMillis;
mNativeToJavaJniLatencyMillis = builder.mNativeToJavaJniLatencyMillis;
+ mJoinType = builder.mJoinType;
+ mNativeNumJoinedResultsCurrentPage = builder.mNativeNumJoinedResultsCurrentPage;
+ mNativeJoinLatencyMillis = builder.mNativeJoinLatencyMillis;
}
/** Returns the package name of the session. */
@@ -317,6 +327,21 @@
return mNativeToJavaJniLatencyMillis;
}
+ /** Returns the type of join performed. Blank if no join is performed */
+ public @JoinableValueType int getJoinType() {
+ return mJoinType;
+ }
+
+ /** Returns the total number of joined documents in the current page. */
+ public int getNumJoinedResultsCurrentPage() {
+ return mNativeNumJoinedResultsCurrentPage;
+ }
+
+ /** Returns the time taken to join documents together. */
+ public int getJoinLatencyMillis() {
+ return mNativeJoinLatencyMillis;
+ }
+
/** Builder for {@link SearchStats} */
public static class Builder {
@NonNull
@@ -349,7 +374,9 @@
int mNativeLockAcquisitionLatencyMillis;
int mJavaToNativeJniLatencyMillis;
int mNativeToJavaJniLatencyMillis;
-
+ @JoinableValueType int mJoinType;
+ int mNativeNumJoinedResultsCurrentPage;
+ int mNativeJoinLatencyMillis;
/**
* Constructor
@@ -363,13 +390,15 @@
}
/** Sets the database used by the session. */
+ @CanIgnoreReturnValue
@NonNull
- public Builder setDatabase(@NonNull String database) {
- mDatabase = Preconditions.checkNotNull(database);
+ public Builder setDatabase(@Nullable String database) {
+ mDatabase = database;
return this;
}
/** Sets the status of the search. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -377,6 +406,7 @@
}
/** Sets total latency for the search. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -384,6 +414,7 @@
}
/** Sets time used to rewrite the search spec. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis) {
mRewriteSearchSpecLatencyMillis = rewriteSearchSpecLatencyMillis;
@@ -391,6 +422,7 @@
}
/** Sets time used to rewrite the search results. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis) {
mRewriteSearchResultLatencyMillis = rewriteSearchResultLatencyMillis;
@@ -398,6 +430,7 @@
}
/** Sets time passed while waiting to acquire the lock during Java function calls. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setJavaLockAcquisitionLatencyMillis(int javaLockAcquisitionLatencyMillis) {
mJavaLockAcquisitionLatencyMillis = javaLockAcquisitionLatencyMillis;
@@ -408,6 +441,7 @@
* Sets time spent on ACL checking, which is the time spent filtering namespaces based on
* package permissions and Android permission access.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setAclCheckLatencyMillis(int aclCheckLatencyMillis) {
mAclCheckLatencyMillis = aclCheckLatencyMillis;
@@ -415,6 +449,7 @@
}
/** Sets overall time used for the native function calls. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
mNativeLatencyMillis = nativeLatencyMillis;
@@ -422,6 +457,7 @@
}
/** Sets number of terms in the search string. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTermCount(int termCount) {
mNativeNumTerms = termCount;
@@ -429,6 +465,7 @@
}
/** Sets length of the search string. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setQueryLength(int queryLength) {
mNativeQueryLength = queryLength;
@@ -436,6 +473,7 @@
}
/** Sets number of namespaces filtered. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setFilteredNamespaceCount(int filteredNamespaceCount) {
mNativeNumNamespacesFiltered = filteredNamespaceCount;
@@ -443,6 +481,7 @@
}
/** Sets number of schema types filtered. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setFilteredSchemaTypeCount(int filteredSchemaTypeCount) {
mNativeNumSchemaTypesFiltered = filteredSchemaTypeCount;
@@ -450,6 +489,7 @@
}
/** Sets the requested number of results in one page. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRequestedPageSize(int requestedPageSize) {
mNativeRequestedPageSize = requestedPageSize;
@@ -457,6 +497,7 @@
}
/** Sets the actual number of results returned in the current page. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setCurrentPageReturnedResultCount(
int currentPageReturnedResultCount) {
@@ -469,6 +510,7 @@
* not, Icing will fetch the results from cache so that some steps
* may be skipped.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setIsFirstPage(boolean nativeIsFirstPage) {
mNativeIsFirstPage = nativeIsFirstPage;
@@ -479,6 +521,7 @@
* Sets time used to parse the query, including 2 parts: tokenizing and
* transforming tokens into an iterator tree.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setParseQueryLatencyMillis(int parseQueryLatencyMillis) {
mNativeParseQueryLatencyMillis = parseQueryLatencyMillis;
@@ -486,6 +529,7 @@
}
/** Sets strategy of scoring and ranking. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRankingStrategy(
@SearchSpec.RankingStrategy int rankingStrategy) {
@@ -494,6 +538,7 @@
}
/** Sets number of documents scored. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setScoredDocumentCount(int scoredDocumentCount) {
mNativeNumDocumentsScored = scoredDocumentCount;
@@ -501,6 +546,7 @@
}
/** Sets time used to score the raw results. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setScoringLatencyMillis(int scoringLatencyMillis) {
mNativeScoringLatencyMillis = scoringLatencyMillis;
@@ -508,6 +554,7 @@
}
/** Sets time used to rank the scored results. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRankingLatencyMillis(int rankingLatencyMillis) {
mNativeRankingLatencyMillis = rankingLatencyMillis;
@@ -515,6 +562,7 @@
}
/** Sets time used to fetch the document protos. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocumentRetrievingLatencyMillis(
int documentRetrievingLatencyMillis) {
@@ -523,6 +571,7 @@
}
/** Sets how many snippets are calculated. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setResultWithSnippetsCount(int resultWithSnippetsCount) {
mNativeNumResultsWithSnippets = resultWithSnippetsCount;
@@ -530,6 +579,7 @@
}
/** Sets time passed while waiting to acquire the lock during native function calls. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeLockAcquisitionLatencyMillis(
int nativeLockAcquisitionLatencyMillis) {
@@ -538,6 +588,7 @@
}
/** Sets time used to send data across the JNI boundary from java to native side. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setJavaToNativeJniLatencyMillis(int javaToNativeJniLatencyMillis) {
mJavaToNativeJniLatencyMillis = javaToNativeJniLatencyMillis;
@@ -545,12 +596,34 @@
}
/** Sets time used to send data across the JNI boundary from native to java side. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNativeToJavaJniLatencyMillis(int nativeToJavaJniLatencyMillis) {
mNativeToJavaJniLatencyMillis = nativeToJavaJniLatencyMillis;
return this;
}
+ /** Sets whether or not this is a join query */
+ @NonNull
+ public Builder setJoinType(@JoinableValueType int joinType) {
+ mJoinType = joinType;
+ return this;
+ }
+
+ /** Set the total number of joined documents in a page. */
+ @NonNull
+ public Builder setNativeNumJoinedResultsCurrentPage(int nativeNumJoinedResultsCurrentPage) {
+ mNativeNumJoinedResultsCurrentPage = nativeNumJoinedResultsCurrentPage;
+ return this;
+ }
+
+ /** Sets time it takes to join documents together in icing. */
+ @NonNull
+ public Builder setNativeJoinLatencyMillis(int nativeJoinLatencyMillis) {
+ mNativeJoinLatencyMillis = nativeJoinLatencyMillis;
+ return this;
+ }
+
/**
* Constructs a new {@link SearchStats} from the contents of this
* {@link SearchStats.Builder}.
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SetSchemaStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SetSchemaStats.java
index c052eb8..6ff7d8e 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SetSchemaStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SetSchemaStats.java
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.appsearch.stats.SchemaMigrationStats;
import androidx.core.util.Preconditions;
@@ -224,7 +225,8 @@
}
/** Gets the type indicate how this set schema call relative to schema migration cases */
- public @SchemaMigrationStats.SchemaMigrationCallType int getSchemaMigrationCallType() {
+ @SchemaMigrationStats.SchemaMigrationCallType
+ public int getSchemaMigrationCallType() {
return mSchemaMigrationCallType;
}
@@ -266,6 +268,7 @@
}
/** Sets the status of the SetSchema action. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mStatusCode = statusCode;
@@ -273,6 +276,7 @@
}
/** Sets total latency for the SetSchema action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
@@ -280,6 +284,7 @@
}
/** Sets number of new types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNewTypeCount(int newTypeCount) {
mNewTypeCount = newTypeCount;
@@ -287,6 +292,7 @@
}
/** Sets number of deleted types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDeletedTypeCount(int deletedTypeCount) {
mDeletedTypeCount = deletedTypeCount;
@@ -294,6 +300,7 @@
}
/** Sets number of compatible type changes. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setCompatibleTypeChangeCount(int compatibleTypeChangeCount) {
mCompatibleTypeChangeCount = compatibleTypeChangeCount;
@@ -301,6 +308,7 @@
}
/** Sets number of index-incompatible type changes. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setIndexIncompatibleTypeChangeCount(int indexIncompatibleTypeChangeCount) {
mIndexIncompatibleTypeChangeCount = indexIncompatibleTypeChangeCount;
@@ -308,6 +316,7 @@
}
/** Sets number of backwards-incompatible type changes. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setBackwardsIncompatibleTypeChangeCount(
int backwardsIncompatibleTypeChangeCount) {
@@ -316,6 +325,7 @@
}
/** Sets total latency for the SetSchema in native action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setVerifyIncomingCallLatencyMillis(int verifyIncomingCallLatencyMillis) {
mVerifyIncomingCallLatencyMillis = verifyIncomingCallLatencyMillis;
@@ -323,6 +333,7 @@
}
/** Sets total latency for the SetSchema in native action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setExecutorAcquisitionLatencyMillis(int executorAcquisitionLatencyMillis) {
mExecutorAcquisitionLatencyMillis = executorAcquisitionLatencyMillis;
@@ -330,6 +341,7 @@
}
/** Sets latency for the rebuild schema object from bundle action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRebuildFromBundleLatencyMillis(int rebuildFromBundleLatencyMillis) {
mRebuildFromBundleLatencyMillis = rebuildFromBundleLatencyMillis;
@@ -339,6 +351,7 @@
/**
* Sets latency for waiting to acquire the lock during Java function calls in milliseconds.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setJavaLockAcquisitionLatencyMillis(int javaLockAcquisitionLatencyMillis) {
mJavaLockAcquisitionLatencyMillis = javaLockAcquisitionLatencyMillis;
@@ -346,6 +359,7 @@
}
/** Sets latency for the rewrite the schema proto action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRewriteSchemaLatencyMillis(int rewriteSchemaLatencyMillis) {
mRewriteSchemaLatencyMillis = rewriteSchemaLatencyMillis;
@@ -353,6 +367,7 @@
}
/** Sets total latency for a single set schema in native action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalNativeLatencyMillis(int totalNativeLatencyMillis) {
mTotalNativeLatencyMillis = totalNativeLatencyMillis;
@@ -360,6 +375,7 @@
}
/** Sets latency for the apply visibility settings action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setVisibilitySettingLatencyMillis(int visibilitySettingLatencyMillis) {
mVisibilitySettingLatencyMillis = visibilitySettingLatencyMillis;
@@ -367,6 +383,7 @@
}
/** Sets latency for converting to SetSchemaResponseInternal object in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setConvertToResponseLatencyMillis(int convertToResponseLatencyMillis) {
mConvertToResponseLatencyMillis = convertToResponseLatencyMillis;
@@ -374,6 +391,7 @@
}
/** Sets latency for the dispatch change notification action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setDispatchChangeNotificationsLatencyMillis(
int dispatchChangeNotificationsLatencyMillis) {
@@ -382,6 +400,7 @@
}
/** Sets latency for the optimization action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setOptimizeLatencyMillis(int optimizeLatencyMillis) {
mOptimizeLatencyMillis = optimizeLatencyMillis;
@@ -389,6 +408,7 @@
}
/** Sets whether this package is observed and we should prepare change notifications. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setIsPackageObserved(boolean isPackageObserved) {
mIsPackageObserved = isPackageObserved;
@@ -396,6 +416,7 @@
}
/** Sets latency for the old schema action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setGetOldSchemaLatencyMillis(int getOldSchemaLatencyMillis) {
mGetOldSchemaLatencyMillis = getOldSchemaLatencyMillis;
@@ -403,6 +424,7 @@
}
/** Sets latency for the registered observer action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setGetObserverLatencyMillis(int getObserverLatencyMillis) {
mGetObserverLatencyMillis = getObserverLatencyMillis;
@@ -410,6 +432,7 @@
}
/** Sets latency for the preparing change notification action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setPreparingChangeNotificationLatencyMillis(
int preparingChangeNotificationLatencyMillis) {
@@ -418,6 +441,7 @@
}
/** Sets the type indicate how this set schema call relative to schema migration cases */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSchemaMigrationCallType(
@SchemaMigrationStats.SchemaMigrationCallType int schemaMigrationCallType) {
diff --git a/appsearch/appsearch-platform-storage/build.gradle b/appsearch/appsearch-platform-storage/build.gradle
index 83ec8c6..285ae41 100644
--- a/appsearch/appsearch-platform-storage/build.gradle
+++ b/appsearch/appsearch-platform-storage/build.gradle
@@ -27,7 +27,7 @@
implementation project(":appsearch:appsearch")
implementation('androidx.collection:collection:1.2.0')
implementation("androidx.concurrent:concurrent-futures:1.0.0")
- implementation("androidx.core:core:1.8.0")
+ implementation(project(":core:core"))
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRules)
diff --git a/appsearch/appsearch-platform-storage/lint-baseline.xml b/appsearch/appsearch-platform-storage/lint-baseline.xml
index d82c712..c09fd6a 100644
--- a/appsearch/appsearch-platform-storage/lint-baseline.xml
+++ b/appsearch/appsearch-platform-storage/lint-baseline.xml
@@ -3,7 +3,7 @@
<issue
id="WrongConstant"
- message="Must be one of: AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR"
+ message="Must be one of: AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR, AppSearchResult.RESULT_DENIED, but could be AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR"
errorLine1=" platformResult.getResultCode(), platformResult.getErrorMessage()));"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -12,7 +12,7 @@
<issue
id="WrongConstant"
- message="Must be one of: AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR"
+ message="Must be one of: AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR, AppSearchResult.RESULT_DENIED, but could be AppSearchResult.RESULT_OK, AppSearchResult.RESULT_UNKNOWN_ERROR, AppSearchResult.RESULT_INTERNAL_ERROR, AppSearchResult.RESULT_INVALID_ARGUMENT, AppSearchResult.RESULT_IO_ERROR, AppSearchResult.RESULT_OUT_OF_SPACE, AppSearchResult.RESULT_NOT_FOUND, AppSearchResult.RESULT_INVALID_SCHEMA, AppSearchResult.RESULT_SECURITY_ERROR"
errorLine1=" namespaceResult.getResultCode(),"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -148,6 +148,24 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1="public class SearchSuggestionResultToPlatformConverter {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionResultToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1="public final class SearchSuggestionSpecToPlatformConverter {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1="public final class SetSchemaRequestToPlatformConverter {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -155,99 +173,9 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.GetSchemaResponseToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformResponse.getSchemaTypesNotDisplayedBySystem()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.GetSchemaResponseToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformResponse.getRequiredPermissionsForSchemaTypeVisibility().entrySet()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.GetSchemaResponseToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformResponse.getSchemaTypesVisibleToPackages();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.GlobalSearchSessionImpl is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPlatformSession.getByDocumentId(packageName, databaseName,"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.GlobalSearchSessionImpl is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPlatformSession.getSchema("
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.GlobalSearchSessionImpl is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPlatformSession.registerObserverCallback("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.GlobalSearchSessionImpl is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPlatformSession.unregisterObserverCallback(targetPackageName, frameworkCallback);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.SearchResultToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformMatchInfo.getSubmatchRange().getStart(),"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.SearchResultToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformMatchInfo.getSubmatchRange().getEnd()));"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 33; however, the containing class androidx.appsearch.platformstorage.converter.SetSchemaRequestToPlatformConverter is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" platformBuilder.addRequiredPermissionsForSchemaTypeVisibility("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java"/>
- </issue>
-
- <issue
id="PrereleaseSdkCoreDependency"
- message="Prelease SDK check isAtLeastT cannot be called as this project has a versioned dependency on androidx.core:core"
- errorLine1=" return BuildCompat.isAtLeastT();"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" return BuildCompat.isAtLeastU();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java"/>
@@ -255,20 +183,92 @@
<issue
id="PrereleaseSdkCoreDependency"
- message="Prelease SDK check isAtLeastT cannot be called as this project has a versioned dependency on androidx.core:core"
- errorLine1=" if (BuildCompat.isAtLeastT()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java"/>
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java"/>
</issue>
<issue
id="PrereleaseSdkCoreDependency"
- message="Prelease SDK check isAtLeastT cannot be called as this project has a versioned dependency on androidx.core:core"
- errorLine1=" if (BuildCompat.isAtLeastT()) {"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java"/>
</issue>
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU() && mSearchSpec.getJoinSpec() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/SearchResultsImpl.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (!BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java"/>
+ </issue>
+
</issues>
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index 2f9336e..e52dd19 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -15,6 +15,9 @@
*/
package androidx.appsearch.platformstorage;
+import android.annotation.SuppressLint;
+import android.os.Build;
+
import androidx.annotation.NonNull;
import androidx.appsearch.app.Features;
import androidx.core.os.BuildCompat;
@@ -26,7 +29,8 @@
final class FeaturesImpl implements Features {
@Override
- // TODO(b/201316758): Remove once BuildCompat.isAtLeastT is removed
+ // TODO(b/265311462): Remove these two lines once BuildCompat.isAtLeastU() is removed
+ @SuppressLint("NewApi")
@BuildCompat.PrereleaseSdkCheck
public boolean isFeatureSupported(@NonNull String feature) {
switch (feature) {
@@ -40,37 +44,33 @@
case Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK:
// fall through
case Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH:
- // fall through
- return BuildCompat.isAtLeastT();
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
// Android U Features
- case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
- // TODO(b/203700301) : Update to reflect support in Android U+ once this feature is
- // synced over into service-appsearch.
- // fall through
- case Features.TOKENIZER_TYPE_RFC822:
- // TODO(b/259294369) : Update to reflect support in Android U+ once this feature is
- // synced over into service-appsearch.
- // fall through
- case Features.NUMERIC_SEARCH:
- // TODO(b/259744228) : Update to reflect support in Android U+ once this feature is
- // synced over into service-appsearch.
- // fall through
- case SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
- // TODO(b/261474063) : Update to reflect support in Android U+ once advanced
- // ranking becomes available.
- // fall through
case Features.JOIN_SPEC_AND_QUALIFIED_ID:
- // TODO(b/256022027) : Update to reflect support in Android U+ once this feature is
- // synced over into service-appsearch.
- // fall through
- case Features.VERBATIM_SEARCH:
- // TODO(b/204333391) : Update to reflect support in Android U+ once this feature is
- // synced over into service-appsearch.
// fall through
case Features.LIST_FILTER_QUERY_LANGUAGE:
- // TODO(b/208654892) : Update to reflect support in Android U+ once this feature is
+ // fall through
+ case Features.NUMERIC_SEARCH:
+ // fall through
+ case Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
+ // fall through
+ case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
+ // fall through
+ case Features.SEARCH_SUGGESTION:
+ // fall through
+ case Features.TOKENIZER_TYPE_RFC822:
+ // fall through
+ case Features.VERBATIM_SEARCH:
+ return BuildCompat.isAtLeastU();
+
+ // Beyond Android U features
+ case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
+ // TODO(b/258715421) : Update to reflect support in Android U+ once this feature is
// synced over into service-appsearch.
+ // fall through
+ case Features.SCHEMA_SET_DELETION_PROPAGATION:
+ // TODO(b/268521214) : Update when feature is ready in service-appsearch.
return false;
default:
return false;
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java
index 1ee50ec..dbebde8 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/GlobalSearchSessionImpl.java
@@ -16,8 +16,11 @@
package androidx.appsearch.platformstorage;
import android.annotation.SuppressLint;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.BatchResultCallback;
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -52,9 +55,10 @@
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
- * An implementation of {@link androidx.appsearch.app.GlobalSearchSession} which proxies to a
+ * An implementation of {@link GlobalSearchSession} which proxies to a
* platform {@link android.app.appsearch.GlobalSearchSession}.
*
* @hide
@@ -80,7 +84,6 @@
mFeatures = Preconditions.checkNotNull(features);
}
- @BuildCompat.PrereleaseSdkCheck
@NonNull
@Override
public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
@@ -95,14 +98,16 @@
Preconditions.checkNotNull(request);
ResolvableFuture<AppSearchBatchResult<String, GenericDocument>> future =
ResolvableFuture.create();
- mPlatformSession.getByDocumentId(packageName, databaseName,
- RequestToPlatformConverter.toPlatformGetByDocumentIdRequest(request),
- mExecutor,
+ ApiHelperForT.getByDocumentId(mPlatformSession, packageName, databaseName,
+ RequestToPlatformConverter.toPlatformGetByDocumentIdRequest(request), mExecutor,
new BatchResultCallbackAdapter<>(
future, GenericDocumentToPlatformConverter::toJetpackGenericDocument));
return future;
}
+ // TODO(b/265311462): Remove these two lines once BuildCompat.isAtLeastU() is removed
+ @SuppressLint("NewApi")
+ @BuildCompat.PrereleaseSdkCheck
@Override
@NonNull
public SearchResults search(
@@ -131,6 +136,8 @@
return future;
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
@BuildCompat.PrereleaseSdkCheck
@NonNull
@Override
@@ -144,10 +151,7 @@
+ " is not supported on this AppSearch implementation.");
}
ResolvableFuture<GetSchemaResponse> future = ResolvableFuture.create();
- mPlatformSession.getSchema(
- packageName,
- databaseName,
- mExecutor,
+ ApiHelperForT.getSchema(mPlatformSession, packageName, databaseName, mExecutor,
result -> AppSearchResultToPlatformConverter.platformAppSearchResultToFuture(
result,
future,
@@ -161,9 +165,7 @@
return mFeatures;
}
- // TODO(b/193494000): Remove these two lines once BuildCompat.isAtLeastT() is removed.
- @SuppressLint("NewApi")
- @BuildCompat.PrereleaseSdkCheck
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Override
public void registerObserverCallback(
@NonNull String targetPackageName,
@@ -213,10 +215,8 @@
// Regardless of whether this stub was fresh or not, we have to register it again
// because the user might be supplying a different spec.
try {
- mPlatformSession.registerObserverCallback(
- targetPackageName,
- ObserverSpecToPlatformConverter.toPlatformObserverSpec(spec),
- executor,
+ ApiHelperForT.registerObserverCallback(mPlatformSession, targetPackageName,
+ ObserverSpecToPlatformConverter.toPlatformObserverSpec(spec), executor,
frameworkCallback);
} catch (android.app.appsearch.exceptions.AppSearchException e) {
throw new AppSearchException((int) e.getResultCode(), e.getMessage(), e.getCause());
@@ -229,8 +229,6 @@
}
}
- @SuppressLint("NewApi")
- @BuildCompat.PrereleaseSdkCheck
@Override
public void unregisterObserverCallback(
@NonNull String targetPackageName, @NonNull ObserverCallback observer)
@@ -253,7 +251,8 @@
}
try {
- mPlatformSession.unregisterObserverCallback(targetPackageName, frameworkCallback);
+ ApiHelperForT.unregisterObserverCallback(mPlatformSession, targetPackageName,
+ frameworkCallback);
} catch (android.app.appsearch.exceptions.AppSearchException e) {
throw new AppSearchException((int) e.getResultCode(), e.getMessage(), e.getCause());
}
@@ -267,4 +266,43 @@
public void close() {
mPlatformSession.close();
}
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static class ApiHelperForT {
+ private ApiHelperForT() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void getByDocumentId(android.app.appsearch.GlobalSearchSession platformSession,
+ String packageName, String databaseName,
+ android.app.appsearch.GetByDocumentIdRequest request, Executor executor,
+ BatchResultCallback<String, android.app.appsearch.GenericDocument> callback) {
+ platformSession.getByDocumentId(packageName, databaseName, request, executor, callback);
+ }
+
+ @DoNotInline
+ static void getSchema(android.app.appsearch.GlobalSearchSession platformSessions,
+ String packageName, String databaseName, Executor executor,
+ Consumer<AppSearchResult<android.app.appsearch.GetSchemaResponse>> callback) {
+ platformSessions.getSchema(packageName, databaseName, executor, callback);
+ }
+
+ @DoNotInline
+ static void registerObserverCallback(
+ android.app.appsearch.GlobalSearchSession platformSession, String targetPackageName,
+ android.app.appsearch.observer.ObserverSpec spec, Executor executor,
+ android.app.appsearch.observer.ObserverCallback observer)
+ throws android.app.appsearch.exceptions.AppSearchException {
+ platformSession.registerObserverCallback(targetPackageName, spec, executor, observer);
+ }
+
+ @DoNotInline
+ static void unregisterObserverCallback(
+ android.app.appsearch.GlobalSearchSession platformSession, String targetPackageName,
+ android.app.appsearch.observer.ObserverCallback observer)
+ throws android.app.appsearch.exceptions.AppSearchException {
+ platformSession.unregisterObserverCallback(targetPackageName, observer);
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchResultsImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchResultsImpl.java
index 6fde6d6..1217bf6 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchResultsImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchResultsImpl.java
@@ -58,13 +58,14 @@
mExecutor = Preconditions.checkNotNull(executor);
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
@SuppressLint("WrongConstant")
@Override
@NonNull
@BuildCompat.PrereleaseSdkCheck
public ListenableFuture<List<SearchResult>> getNextPageAsync() {
- // TODO(b/256022027): add isAtLeastU check after Android U.
- if (mSearchSpec.getJoinSpec() != null) {
+ if (!BuildCompat.isAtLeastU() && mSearchSpec.getJoinSpec() != null) {
throw new UnsupportedOperationException("Searching with a SearchSpec containing a "
+ "JoinSpec is not supported on this AppSearch implementation.");
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
index 4e4f3db..06db2c0 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
@@ -15,8 +15,11 @@
*/
package androidx.appsearch.platformstorage;
+import android.annotation.SuppressLint;
+import android.app.appsearch.AppSearchResult;
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
@@ -43,6 +46,8 @@
import androidx.appsearch.platformstorage.converter.RequestToPlatformConverter;
import androidx.appsearch.platformstorage.converter.ResponseToPlatformConverter;
import androidx.appsearch.platformstorage.converter.SearchSpecToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.SearchSuggestionResultToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.SearchSuggestionSpecToPlatformConverter;
import androidx.appsearch.platformstorage.converter.SetSchemaRequestToPlatformConverter;
import androidx.appsearch.platformstorage.util.BatchResultCallbackAdapter;
import androidx.concurrent.futures.ResolvableFuture;
@@ -54,6 +59,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* An implementation of {@link AppSearchSession} which proxies to a platform
@@ -76,9 +82,11 @@
mFeatures = Preconditions.checkNotNull(features);
}
+ // TODO(b/265311462): Remove these two lines once BuildCompat.isAtLeastU() is removed
+ @SuppressLint("NewApi")
+ @BuildCompat.PrereleaseSdkCheck
@Override
@NonNull
- @BuildCompat.PrereleaseSdkCheck
public ListenableFuture<SetSchemaResponse> setSchemaAsync(@NonNull SetSchemaRequest request) {
Preconditions.checkNotNull(request);
ResolvableFuture<SetSchemaResponse> future = ResolvableFuture.create();
@@ -93,9 +101,11 @@
return future;
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@Override
@NonNull
- @BuildCompat.PrereleaseSdkCheck
public ListenableFuture<GetSchemaResponse> getSchemaAsync() {
ResolvableFuture<GetSchemaResponse> future = ResolvableFuture.create();
mPlatformSession.getSchema(
@@ -146,6 +156,9 @@
return future;
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@Override
@NonNull
public SearchResults search(
@@ -160,13 +173,34 @@
return new SearchResultsImpl(platformSearchResults, searchSpec, mExecutor);
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
@Override
public ListenableFuture<List<SearchSuggestionResult>> searchSuggestionAsync(
- @NonNull String suggestionQueryExpression, @NonNull SearchSuggestionSpec searchSpec) {
- // TODO(b/227356108) Implement this after we export to framework.
- throw new UnsupportedOperationException(
- "Search Suggestion is not supported on this AppSearch implementation.");
+ @NonNull String suggestionQueryExpression,
+ @NonNull SearchSuggestionSpec searchSuggestionSpec) {
+ Preconditions.checkNotNull(suggestionQueryExpression);
+ Preconditions.checkNotNull(searchSuggestionSpec);
+ if (BuildCompat.isAtLeastU()) {
+ ResolvableFuture<List<SearchSuggestionResult>> future = ResolvableFuture.create();
+ ApiHelperForU.searchSuggestion(
+ mPlatformSession,
+ suggestionQueryExpression,
+ SearchSuggestionSpecToPlatformConverter
+ .toPlatformSearchSuggestionSpec(searchSuggestionSpec),
+ mExecutor,
+ result -> AppSearchResultToPlatformConverter.platformAppSearchResultToFuture(
+ result,
+ future,
+ SearchSuggestionResultToPlatformConverter
+ ::toJetpackSearchSuggestionResults));
+ return future;
+ } else {
+ throw new UnsupportedOperationException(
+ "Search Suggestion is not supported on this AppSearch implementation.");
+ }
}
@Override
@@ -195,9 +229,11 @@
return future;
}
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@Override
@NonNull
- @BuildCompat.PrereleaseSdkCheck
public ListenableFuture<Void> removeAsync(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
Preconditions.checkNotNull(queryExpression);
@@ -295,4 +331,23 @@
public void close() {
mPlatformSession.close();
}
+
+ @RequiresApi(34)
+ static class ApiHelperForU {
+ private ApiHelperForU() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void searchSuggestion(
+ @NonNull android.app.appsearch.AppSearchSession appSearchSession,
+ @NonNull String suggestionQueryExpression,
+ @NonNull android.app.appsearch.SearchSuggestionSpec searchSuggestionSpec,
+ @NonNull Executor executor,
+ @NonNull Consumer<AppSearchResult<
+ List<android.app.appsearch.SearchSuggestionResult>>> callback) {
+ appSearchSession.searchSuggestion(suggestionQueryExpression, searchSuggestionSpec,
+ executor, callback);
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
index 05575ce..27b1c19 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
@@ -18,6 +18,7 @@
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
@@ -43,8 +44,10 @@
* Translates a platform {@link android.app.appsearch.GetSchemaResponse} into a jetpack
* {@link GetSchemaResponse}.
*/
- @NonNull
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
@BuildCompat.PrereleaseSdkCheck
+ @NonNull
public static GetSchemaResponse toJetpackGetSchemaResponse(
@NonNull android.app.appsearch.GetSchemaResponse platformResponse) {
Preconditions.checkNotNull(platformResponse);
@@ -60,17 +63,18 @@
jetpackBuilder.addSchema(SchemaToPlatformConverter.toJetpackSchema(platformSchema));
}
jetpackBuilder.setVersion(platformResponse.getVersion());
- if (BuildCompat.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Convert schemas not displayed by system
for (String schemaTypeNotDisplayedBySystem :
- platformResponse.getSchemaTypesNotDisplayedBySystem()) {
+ ApiHelperForT.getSchemaTypesNotDisplayedBySystem(platformResponse)) {
jetpackBuilder.addSchemaTypeNotDisplayedBySystem(schemaTypeNotDisplayedBySystem);
}
// Convert schemas visible to packages
convertSchemasVisibleToPackages(platformResponse, jetpackBuilder);
// Convert schemas visible to permissions
for (Map.Entry<String, Set<Set<Integer>>> entry :
- platformResponse.getRequiredPermissionsForSchemaTypeVisibility().entrySet()) {
+ ApiHelperForT.getRequiredPermissionsForSchemaTypeVisibility(platformResponse)
+ .entrySet()) {
jetpackBuilder.setRequiredPermissionsForSchemaTypeVisibility(entry.getKey(),
entry.getValue());
}
@@ -90,7 +94,7 @@
// incorrectly returns {@code null} in some prerelease versions of Android T. Remove
// this workaround after the issue is fixed in T.
Map<String, Set<android.app.appsearch.PackageIdentifier>> schemaTypesVisibleToPackages =
- platformResponse.getSchemaTypesVisibleToPackages();
+ ApiHelperForT.getSchemaTypesVisibleToPackage(platformResponse);
if (schemaTypesVisibleToPackages != null) {
for (Map.Entry<String, Set<android.app.appsearch.PackageIdentifier>> entry
: schemaTypesVisibleToPackages.entrySet()) {
@@ -107,4 +111,30 @@
}
}
}
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static class ApiHelperForT {
+ private ApiHelperForT() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Set<String> getSchemaTypesNotDisplayedBySystem(
+ android.app.appsearch.GetSchemaResponse platformResponse) {
+ return platformResponse.getSchemaTypesNotDisplayedBySystem();
+ }
+
+ @DoNotInline
+ static Map<String, Set<android.app.appsearch.PackageIdentifier>>
+ getSchemaTypesVisibleToPackage(
+ android.app.appsearch.GetSchemaResponse platformResponse) {
+ return platformResponse.getSchemaTypesVisibleToPackages();
+ }
+
+ @DoNotInline
+ static Map<String, Set<Set<Integer>>> getRequiredPermissionsForSchemaTypeVisibility(
+ android.app.appsearch.GetSchemaResponse platformResponse) {
+ return platformResponse.getRequiredPermissionsForSchemaTypeVisibility();
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/JoinSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/JoinSpecToPlatformConverter.java
new file mode 100644
index 0000000..9696cec
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/JoinSpecToPlatformConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.platformstorage.converter;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.JoinSpec;
+import androidx.core.os.BuildCompat;
+import androidx.core.util.Preconditions;
+
+/**
+ * Translates between Platform and Jetpack versions of {@link JoinSpec}.
+ */
+// TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once
+// SearchSpecToPlatformConverter.toPlatformSearchSpec() removes it. Also, replace literal '34' with
+// Build.VERSION_CODES.UPSIDE_DOWN_CAKE once the SDK_INT is finalized.
+@BuildCompat.PrereleaseSdkCheck
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RequiresApi(34)
+public class JoinSpecToPlatformConverter {
+ private JoinSpecToPlatformConverter() {}
+
+ /**
+ * Translates a Jetpack {@link JoinSpec} into a platform {@link android.app.appsearch.JoinSpec}.
+ */
+ @SuppressLint("WrongConstant")
+ @NonNull
+ public static android.app.appsearch.JoinSpec toPlatformJoinSpec(@NonNull JoinSpec jetpackSpec) {
+ Preconditions.checkNotNull(jetpackSpec);
+ return new android.app.appsearch.JoinSpec.Builder(jetpackSpec.getChildPropertyExpression())
+ .setNestedSearch(
+ jetpackSpec.getNestedQuery(),
+ SearchSpecToPlatformConverter.toPlatformSearchSpec(
+ jetpackSpec.getNestedSearchSpec()))
+ .setMaxJoinedResultCount(jetpackSpec.getMaxJoinedResultCount())
+ .setAggregationScoringStrategy(jetpackSpec.getAggregationScoringStrategy())
+ .build();
+ }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
index eba43cf..9015bef 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
@@ -19,16 +19,18 @@
import android.annotation.SuppressLint;
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.appsearch.app.AppSearchSchema;
+import androidx.core.os.BuildCompat;
import androidx.core.util.Preconditions;
import java.util.List;
/**
- * Translates a jetpack {@link androidx.appsearch.app.AppSearchSchema} into a platform
+ * Translates a jetpack {@link AppSearchSchema} into a platform
* {@link android.app.appsearch.AppSearchSchema}.
* @hide
*/
@@ -38,9 +40,12 @@
private SchemaToPlatformConverter() {}
/**
- * Translates a jetpack {@link androidx.appsearch.app.AppSearchSchema} into a platform
+ * Translates a jetpack {@link AppSearchSchema} into a platform
* {@link android.app.appsearch.AppSearchSchema}.
*/
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once
+ // toPlatformProperty() doesn't have it either.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
public static android.app.appsearch.AppSearchSchema toPlatformSchema(
@NonNull AppSearchSchema jetpackSchema) {
@@ -58,8 +63,11 @@
/**
* Translates a platform {@link android.app.appsearch.AppSearchSchema} to a jetpack
- * {@link androidx.appsearch.app.AppSearchSchema}.
+ * {@link AppSearchSchema}.
*/
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
public static AppSearchSchema toJetpackSchema(
@NonNull android.app.appsearch.AppSearchSchema platformSchema) {
@@ -78,6 +86,9 @@
// Most stringProperty.get calls cause WrongConstant lint errors because the methods are not
// defined as returning the same constants as the corresponding setter expects, but they do
@SuppressLint("WrongConstant")
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
private static android.app.appsearch.AppSearchSchema.PropertyConfig toPlatformProperty(
@NonNull AppSearchSchema.PropertyConfig jetpackProperty) {
@@ -85,34 +96,47 @@
if (jetpackProperty instanceof AppSearchSchema.StringPropertyConfig) {
AppSearchSchema.StringPropertyConfig stringProperty =
(AppSearchSchema.StringPropertyConfig) jetpackProperty;
- // TODO(b/256022027): add isAtLeastU check to allow JOINABLE_VALUE_TYPE_QUALIFIED_ID
- // after Android U, and set joinable value type to PropertyConfig.
- if (stringProperty.getJoinableValueType()
- == AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
- throw new UnsupportedOperationException(
- "StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID is not supported on "
- + "this AppSearch implementation.");
- }
- return new android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder(
+ android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder platformBuilder =
+ new android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder(
stringProperty.getName())
.setCardinality(stringProperty.getCardinality())
.setIndexingType(stringProperty.getIndexingType())
- .setTokenizerType(stringProperty.getTokenizerType())
- .build();
+ .setTokenizerType(stringProperty.getTokenizerType());
+ if (stringProperty.getDeletionPropagation()) {
+ // TODO(b/268521214): Update once deletion propagation is available.
+ throw new UnsupportedOperationException("Setting deletion propagation is not "
+ + "supported on this AppSearch implementation.");
+ }
+
+ if (stringProperty.getJoinableValueType()
+ == AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ "StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID is not supported"
+ + " on this AppSearch implementation.");
+ }
+ ApiHelperForU.setJoinableValueType(platformBuilder,
+ stringProperty.getJoinableValueType());
+ }
+ return platformBuilder.build();
} else if (jetpackProperty instanceof AppSearchSchema.LongPropertyConfig) {
AppSearchSchema.LongPropertyConfig longProperty =
(AppSearchSchema.LongPropertyConfig) jetpackProperty;
- // TODO(b/259744228): add isAtLeastU check to allow INDEXING_TYPE_RANGE after Android U.
+ android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder longPropertyBuilder =
+ new android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder(
+ jetpackProperty.getName())
+ .setCardinality(jetpackProperty.getCardinality());
if (longProperty.getIndexingType()
== AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE) {
- throw new UnsupportedOperationException(
- "LongProperty.INDEXING_TYPE_RANGE is not supported on this AppSearch "
- + "implementation.");
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ "LongProperty.INDEXING_TYPE_RANGE is not supported on this AppSearch "
+ + "implementation.");
+ }
+ ApiHelperForU.setIndexingType(
+ longPropertyBuilder, longProperty.getIndexingType());
}
- return new android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder(
- jetpackProperty.getName())
- .setCardinality(jetpackProperty.getCardinality())
- .build();
+ return longPropertyBuilder.build();
} else if (jetpackProperty instanceof AppSearchSchema.DoublePropertyConfig) {
return new android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder(
jetpackProperty.getName())
@@ -145,6 +169,9 @@
// Most stringProperty.get calls cause WrongConstant lint errors because the methods are not
// defined as returning the same constants as the corresponding setter expects, but they do
@SuppressLint("WrongConstant")
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
private static AppSearchSchema.PropertyConfig toJetpackProperty(
@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig platformProperty) {
@@ -153,16 +180,28 @@
instanceof android.app.appsearch.AppSearchSchema.StringPropertyConfig) {
android.app.appsearch.AppSearchSchema.StringPropertyConfig stringProperty =
(android.app.appsearch.AppSearchSchema.StringPropertyConfig) platformProperty;
- return new AppSearchSchema.StringPropertyConfig.Builder(stringProperty.getName())
- .setCardinality(stringProperty.getCardinality())
- .setIndexingType(stringProperty.getIndexingType())
- .setTokenizerType(stringProperty.getTokenizerType())
- .build();
+ AppSearchSchema.StringPropertyConfig.Builder jetpackBuilder =
+ new AppSearchSchema.StringPropertyConfig.Builder(stringProperty.getName())
+ .setCardinality(stringProperty.getCardinality())
+ .setIndexingType(stringProperty.getIndexingType())
+ .setTokenizerType(stringProperty.getTokenizerType());
+ if (BuildCompat.isAtLeastU()) {
+ jetpackBuilder.setJoinableValueType(
+ ApiHelperForU.getJoinableValueType(stringProperty));
+ }
+ return jetpackBuilder.build();
} else if (platformProperty
instanceof android.app.appsearch.AppSearchSchema.LongPropertyConfig) {
- return new AppSearchSchema.LongPropertyConfig.Builder(platformProperty.getName())
- .setCardinality(platformProperty.getCardinality())
- .build();
+ android.app.appsearch.AppSearchSchema.LongPropertyConfig longProperty =
+ (android.app.appsearch.AppSearchSchema.LongPropertyConfig) platformProperty;
+ AppSearchSchema.LongPropertyConfig.Builder jetpackBuilder =
+ new AppSearchSchema.LongPropertyConfig.Builder(longProperty.getName())
+ .setCardinality(longProperty.getCardinality());
+ if (BuildCompat.isAtLeastU()) {
+ jetpackBuilder.setIndexingType(
+ ApiHelperForU.getIndexingType(longProperty));
+ }
+ return jetpackBuilder.build();
} else if (platformProperty
instanceof android.app.appsearch.AppSearchSchema.DoublePropertyConfig) {
return new AppSearchSchema.DoublePropertyConfig.Builder(platformProperty.getName())
@@ -194,4 +233,47 @@
+ ": " + platformProperty);
}
}
+
+ // TODO(b/265311462): Replace literal '34' with Build.VERSION_CODES.UPSIDE_DOWN_CAKE when the
+ // SDK_INT is finalized.
+ @RequiresApi(34)
+ private static class ApiHelperForU {
+ private ApiHelperForU() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setJoinableValueType(
+ android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder builder,
+ @AppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType) {
+ builder.setJoinableValueType(joinableValueType);
+ }
+
+ // Most stringProperty.get calls cause WrongConstant lint errors because the methods are not
+ // defined as returning the same constants as the corresponding setter expects, but they do
+ @SuppressLint("WrongConstant")
+ @DoNotInline
+ @AppSearchSchema.StringPropertyConfig.JoinableValueType
+ static int getJoinableValueType(
+ android.app.appsearch.AppSearchSchema.StringPropertyConfig stringPropertyConfig) {
+ return stringPropertyConfig.getJoinableValueType();
+ }
+
+ @DoNotInline
+ static void setIndexingType(
+ android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder builder,
+ @AppSearchSchema.LongPropertyConfig.IndexingType int longIndexingType) {
+ builder.setIndexingType(longIndexingType);
+ }
+
+ // Most LongProperty.get calls cause WrongConstant lint errors because the methods are not
+ // defined as returning the same constants as the corresponding setter expects, but they do
+ @SuppressLint("WrongConstant")
+ @DoNotInline
+ @AppSearchSchema.LongPropertyConfig.IndexingType
+ static int getIndexingType(
+ android.app.appsearch.AppSearchSchema.LongPropertyConfig longPropertyConfig) {
+ return longPropertyConfig.getIndexingType();
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java
index 707234d..a36e9cf 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchResultToPlatformConverter.java
@@ -18,6 +18,7 @@
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
@@ -38,6 +39,8 @@
private SearchResultToPlatformConverter() {}
/** Translates from Platform to Jetpack versions of {@link SearchResult}. */
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
@BuildCompat.PrereleaseSdkCheck
@NonNull
public static SearchResult toJetpackSearchResult(
@@ -55,10 +58,15 @@
SearchResult.MatchInfo jetpackMatchInfo = toJetpackMatchInfo(platformMatches.get(i));
builder.addMatchInfo(jetpackMatchInfo);
}
+ if (BuildCompat.isAtLeastU()) {
+ for (android.app.appsearch.SearchResult joinedResult :
+ ApiHelperForU.getJoinedResults(platformResult)) {
+ builder.addJoinedResult(toJetpackSearchResult(joinedResult));
+ }
+ }
return builder.build();
}
- @BuildCompat.PrereleaseSdkCheck
@NonNull
private static SearchResult.MatchInfo toJetpackMatchInfo(
@NonNull android.app.appsearch.SearchResult.MatchInfo platformMatchInfo) {
@@ -73,12 +81,46 @@
new SearchResult.MatchRange(
platformMatchInfo.getSnippetRange().getStart(),
platformMatchInfo.getSnippetRange().getEnd()));
- if (BuildCompat.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
builder.setSubmatchRange(
new SearchResult.MatchRange(
- platformMatchInfo.getSubmatchRange().getStart(),
- platformMatchInfo.getSubmatchRange().getEnd()));
+ ApiHelperForT.getSubmatchRangeStart(platformMatchInfo),
+ ApiHelperForT.getSubmatchRangeEnd(platformMatchInfo)));
}
return builder.build();
}
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static class ApiHelperForT {
+ private ApiHelperForT() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static int getSubmatchRangeStart(@NonNull
+ android.app.appsearch.SearchResult.MatchInfo platformMatchInfo) {
+ return platformMatchInfo.getSubmatchRange().getStart();
+ }
+
+ @DoNotInline
+ static int getSubmatchRangeEnd(@NonNull
+ android.app.appsearch.SearchResult.MatchInfo platformMatchInfo) {
+ return platformMatchInfo.getSubmatchRange().getEnd();
+ }
+ }
+
+ // TODO(b/265311462): Replace literal '34' with Build.VERSION_CODES.UPSIDE_DOWN_CAKE when the
+ // SDK_INT is finalized.
+ @RequiresApi(34)
+ private static class ApiHelperForU {
+ private ApiHelperForU() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static List<android.app.appsearch.SearchResult> getJoinedResults(@NonNull
+ android.app.appsearch.SearchResult result) {
+ return result.getJoinedResults();
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
index cc2bd0a..eedf4cb 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
@@ -19,11 +19,14 @@
import android.annotation.SuppressLint;
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.appsearch.app.Features;
+import androidx.appsearch.app.JoinSpec;
import androidx.appsearch.app.SearchSpec;
+import androidx.core.os.BuildCompat;
import androidx.core.util.Preconditions;
import java.util.List;
@@ -44,35 +47,50 @@
// Most jetpackSearchSpec.get calls cause WrongConstant lint errors because the methods are not
// defined as returning the same constants as the corresponding setter expects, but they do
@SuppressLint("WrongConstant")
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
+ @BuildCompat.PrereleaseSdkCheck
@NonNull
public static android.app.appsearch.SearchSpec toPlatformSearchSpec(
@NonNull SearchSpec jetpackSearchSpec) {
Preconditions.checkNotNull(jetpackSearchSpec);
- if (!jetpackSearchSpec.getAdvancedRankingExpression().isEmpty()) {
- // TODO(b/261474063): Remove this once advanced ranking becomes available.
- throw new UnsupportedOperationException(
- Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION
- + " is not available on this AppSearch implementation.");
- }
-
android.app.appsearch.SearchSpec.Builder platformBuilder =
new android.app.appsearch.SearchSpec.Builder();
+ if (!jetpackSearchSpec.getAdvancedRankingExpression().isEmpty()) {
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION
+ + " is not available on this AppSearch implementation.");
+ }
+ ApiHelperForU.setRankingStrategy(
+ platformBuilder, jetpackSearchSpec.getAdvancedRankingExpression());
+ } else {
+ platformBuilder.setRankingStrategy(jetpackSearchSpec.getRankingStrategy());
+ }
+
platformBuilder
.setTermMatch(jetpackSearchSpec.getTermMatch())
.addFilterSchemas(jetpackSearchSpec.getFilterSchemas())
.addFilterNamespaces(jetpackSearchSpec.getFilterNamespaces())
.addFilterPackageNames(jetpackSearchSpec.getFilterPackageNames())
.setResultCountPerPage(jetpackSearchSpec.getResultCountPerPage())
- .setRankingStrategy(jetpackSearchSpec.getRankingStrategy())
.setOrder(jetpackSearchSpec.getOrder())
.setSnippetCount(jetpackSearchSpec.getSnippetCount())
.setSnippetCountPerProperty(jetpackSearchSpec.getSnippetCountPerProperty())
.setMaxSnippetSize(jetpackSearchSpec.getMaxSnippetSize());
- //TODO(b/262512396): add the enabledFeatures set from the SearchSpec once it is synced
- // across to platform.
if (jetpackSearchSpec.getResultGroupingTypeFlags() != 0) {
+ // TODO(b/258715421): Add Build.VERSION.SDK_INT condition once
+ // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is supported on Android U.
+ if (true) {
+ if ((jetpackSearchSpec.getResultGroupingTypeFlags()
+ & SearchSpec.GROUPING_TYPE_PER_SCHEMA) != 0) {
+ throw new UnsupportedOperationException(
+ Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+ + " is not available on this AppSearch implementation.");
+ }
+ }
platformBuilder.setResultGrouping(
jetpackSearchSpec.getResultGroupingTypeFlags(),
jetpackSearchSpec.getResultGroupingLimit());
@@ -82,13 +100,82 @@
platformBuilder.addProjection(projection.getKey(), projection.getValue());
}
- // TODO(b/203700301) : Update to reflect support in Android U+ once this
- // feature is synced over into service-appsearch.
if (!jetpackSearchSpec.getPropertyWeights().isEmpty()) {
- throw new UnsupportedOperationException(
- "Property weights are not supported with this backend/Android API level "
- + "combination.");
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ "Property weights are not supported with this backend/Android API level "
+ + "combination.");
+ }
+ ApiHelperForU.setPropertyWeights(platformBuilder,
+ jetpackSearchSpec.getPropertyWeights());
+ }
+
+ if (!jetpackSearchSpec.getEnabledFeatures().isEmpty()) {
+ if (jetpackSearchSpec.isNumericSearchEnabled()
+ || jetpackSearchSpec.isVerbatimSearchEnabled()
+ || jetpackSearchSpec.isListFilterQueryLanguageEnabled()) {
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException(
+ "Advanced query features (NUMERIC_SEARCH, VERBATIM_SEARCH and "
+ + "LIST_FILTER_QUERY_LANGUAGE) are not supported with this "
+ + "backend/Android API level combination.");
+ }
+ ApiHelperForU.copyEnabledFeatures(platformBuilder, jetpackSearchSpec);
+ }
+ }
+
+ if (jetpackSearchSpec.getJoinSpec() != null) {
+ if (!BuildCompat.isAtLeastU()) {
+ throw new UnsupportedOperationException("JoinSpec is not available on this "
+ + "AppSearch implementation.");
+ }
+ ApiHelperForU.setJoinSpec(platformBuilder, jetpackSearchSpec.getJoinSpec());
}
return platformBuilder.build();
}
+
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed. Also, replace literal '34' with
+ // Build.VERSION_CODES.UPSIDE_DOWN_CAKE once the SDK_INT is finalized.
+ @BuildCompat.PrereleaseSdkCheck
+ @RequiresApi(34)
+ private static class ApiHelperForU {
+ private ApiHelperForU() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setJoinSpec(@NonNull android.app.appsearch.SearchSpec.Builder builder,
+ JoinSpec jetpackJoinSpec) {
+ builder.setJoinSpec(JoinSpecToPlatformConverter.toPlatformJoinSpec(jetpackJoinSpec));
+ }
+
+ @DoNotInline
+ static void setRankingStrategy(@NonNull android.app.appsearch.SearchSpec.Builder builder,
+ @NonNull String rankingExpression) {
+ builder.setRankingStrategy(rankingExpression);
+ }
+
+ @DoNotInline
+ static void copyEnabledFeatures(@NonNull android.app.appsearch.SearchSpec.Builder builder,
+ @NonNull SearchSpec jetpackSpec) {
+ if (jetpackSpec.isNumericSearchEnabled()) {
+ builder.setNumericSearchEnabled(true);
+ }
+ if (jetpackSpec.isVerbatimSearchEnabled()) {
+ builder.setVerbatimSearchEnabled(true);
+ }
+ if (jetpackSpec.isListFilterQueryLanguageEnabled()) {
+ builder.setListFilterQueryLanguageEnabled(true);
+ }
+ }
+
+ @DoNotInline
+ static void setPropertyWeights(@NonNull android.app.appsearch.SearchSpec.Builder builder,
+ @NonNull Map<String, Map<String, Double>> propertyWeightsMap) {
+ for (Map.Entry<String, Map<String, Double>> entry : propertyWeightsMap.entrySet()) {
+ builder.setPropertyWeights(entry.getKey(), entry.getValue());
+ }
+ }
+ }
}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionResultToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionResultToPlatformConverter.java
new file mode 100644
index 0000000..beebb35
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionResultToPlatformConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.platformstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.SearchSuggestionResult;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translates between Platform and Jetpack versions of {@link SearchSuggestionResult}.
+ *
+ * @hide
+ */
+// TODO(b/227356108) replace literal '34' with Build.VERSION_CODES.U once the SDK_INT is finalized.
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(34)
+public class SearchSuggestionResultToPlatformConverter {
+ private SearchSuggestionResultToPlatformConverter() {}
+
+ /** Translates from Platform to Jetpack versions of {@linkSearchSuggestionResult} */
+ @NonNull
+ public static List<SearchSuggestionResult> toJetpackSearchSuggestionResults(
+ @NonNull List<android.app.appsearch.SearchSuggestionResult>
+ platformSearchSuggestionResults) {
+ Preconditions.checkNotNull(platformSearchSuggestionResults);
+ List<SearchSuggestionResult> jetpackSearchSuggestionResults =
+ new ArrayList<>(platformSearchSuggestionResults.size());
+ for (int i = 0; i < platformSearchSuggestionResults.size(); i++) {
+ jetpackSearchSuggestionResults.add(new SearchSuggestionResult.Builder()
+ .setSuggestedResult(platformSearchSuggestionResults.get(i).getSuggestedResult())
+ .build());
+ }
+ return jetpackSearchSuggestionResults;
+ }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java
new file mode 100644
index 0000000..86f20af
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java
@@ -0,0 +1,67 @@
+/*
+ * 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.appsearch.platformstorage.converter;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.core.util.Preconditions;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates between Platform and Jetpack versions of {@link SearchSuggestionSpec}.
+ *
+ * @hide
+ */
+// TODO(b/227356108) replace literal '34' with Build.VERSION_CODES.U once the SDK_INT is finalized.
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(34)
+public final class SearchSuggestionSpecToPlatformConverter {
+ private SearchSuggestionSpecToPlatformConverter() {
+ }
+
+ /** Translates from Jetpack to Platform version of {@link SearchSuggestionSpec}. */
+ // Most jetpackSearchSuggestionSpec.get calls cause WrongConstant lint errors because the
+ // methods are not defined as returning the same constants as the corresponding setter
+ // expects, but they do
+ @SuppressLint("WrongConstant")
+ @NonNull
+ public static android.app.appsearch.SearchSuggestionSpec toPlatformSearchSuggestionSpec(
+ @NonNull SearchSuggestionSpec jetpackSearchSuggestionSpec) {
+ Preconditions.checkNotNull(jetpackSearchSuggestionSpec);
+
+ android.app.appsearch.SearchSuggestionSpec.Builder platformBuilder =
+ new android.app.appsearch.SearchSuggestionSpec.Builder(
+ jetpackSearchSuggestionSpec.getMaximumResultCount());
+
+ platformBuilder
+ .addFilterNamespaces(jetpackSearchSuggestionSpec.getFilterNamespaces())
+ .addFilterSchemas(jetpackSearchSuggestionSpec.getFilterSchemas())
+ .setRankingStrategy(jetpackSearchSuggestionSpec.getRankingStrategy());
+ for (Map.Entry<String, List<String>> documentIdFilters :
+ jetpackSearchSuggestionSpec.getFilterDocumentIds().entrySet()) {
+ platformBuilder.addFilterDocumentIds(documentIdFilters.getKey(),
+ documentIdFilters.getValue());
+ }
+ return platformBuilder.build();
+ }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
index 48c3ede..0ca4dac 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
@@ -18,6 +18,7 @@
import android.os.Build;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
@@ -47,6 +48,8 @@
* Translates a jetpack {@link SetSchemaRequest} into a platform
* {@link android.app.appsearch.SetSchemaRequest}.
*/
+ // TODO(b/265311462): Remove BuildCompat.PrereleaseSdkCheck annotation once usage of
+ // BuildCompat.isAtLeastU() is removed.
@BuildCompat.PrereleaseSdkCheck
@NonNull
public static android.app.appsearch.SetSchemaRequest toPlatformSetSchemaRequest(
@@ -82,7 +85,7 @@
for (Map.Entry<String, Set<Set<Integer>>> entry :
jetpackRequest.getRequiredPermissionsForSchemaTypeVisibility().entrySet()) {
for (Set<Integer> permissionGroup : entry.getValue()) {
- platformBuilder.addRequiredPermissionsForSchemaTypeVisibility(
+ ApiHelperForT.addRequiredPermissionsForSchemaTypeVisibility(platformBuilder,
entry.getKey(), permissionGroup);
}
}
@@ -163,4 +166,18 @@
}
return jetpackBuilder.build();
}
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static class ApiHelperForT {
+ private ApiHelperForT() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void addRequiredPermissionsForSchemaTypeVisibility(
+ android.app.appsearch.SetSchemaRequest.Builder platformBuilder,
+ String schemaType, Set<Integer> permissions) {
+ platformBuilder.addRequiredPermissionsForSchemaTypeVisibility(schemaType, permissions);
+ }
+ }
}
diff --git a/appsearch/appsearch-play-services-storage/api/current.txt b/appsearch/appsearch-play-services-storage/api/current.txt
new file mode 100644
index 0000000..8d9c55f
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/api/current.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.appsearch.playservicesstorage {
+
+ public final class PlayServicesStorage {
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GlobalSearchSession!> createGlobalSearchSessionAsync(androidx.appsearch.playservicesstorage.PlayServicesStorage.GlobalSearchContext);
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchSession!> createSearchSessionAsync(androidx.appsearch.playservicesstorage.PlayServicesStorage.SearchContext);
+ }
+
+ public static final class PlayServicesStorage.GlobalSearchContext {
+ }
+
+ public static final class PlayServicesStorage.GlobalSearchContext.Builder {
+ ctor public PlayServicesStorage.GlobalSearchContext.Builder(android.content.Context);
+ method public androidx.appsearch.playservicesstorage.PlayServicesStorage.GlobalSearchContext build();
+ }
+
+ public static final class PlayServicesStorage.SearchContext {
+ method public String getDatabaseName();
+ }
+
+ public static final class PlayServicesStorage.SearchContext.Builder {
+ ctor public PlayServicesStorage.SearchContext.Builder(android.content.Context, String);
+ method public androidx.appsearch.playservicesstorage.PlayServicesStorage.SearchContext build();
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/appsearch/appsearch-play-services-storage/api/res-current.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to appsearch/appsearch-play-services-storage/api/res-current.txt
diff --git a/appsearch/appsearch-play-services-storage/api/restricted_current.txt b/appsearch/appsearch-play-services-storage/api/restricted_current.txt
new file mode 100644
index 0000000..8d9c55f
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/api/restricted_current.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.appsearch.playservicesstorage {
+
+ public final class PlayServicesStorage {
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GlobalSearchSession!> createGlobalSearchSessionAsync(androidx.appsearch.playservicesstorage.PlayServicesStorage.GlobalSearchContext);
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchSession!> createSearchSessionAsync(androidx.appsearch.playservicesstorage.PlayServicesStorage.SearchContext);
+ }
+
+ public static final class PlayServicesStorage.GlobalSearchContext {
+ }
+
+ public static final class PlayServicesStorage.GlobalSearchContext.Builder {
+ ctor public PlayServicesStorage.GlobalSearchContext.Builder(android.content.Context);
+ method public androidx.appsearch.playservicesstorage.PlayServicesStorage.GlobalSearchContext build();
+ }
+
+ public static final class PlayServicesStorage.SearchContext {
+ method public String getDatabaseName();
+ }
+
+ public static final class PlayServicesStorage.SearchContext.Builder {
+ ctor public PlayServicesStorage.SearchContext.Builder(android.content.Context, String);
+ method public androidx.appsearch.playservicesstorage.PlayServicesStorage.SearchContext build();
+ }
+
+}
+
diff --git a/appsearch/appsearch-play-services-storage/build.gradle b/appsearch/appsearch-play-services-storage/build.gradle
new file mode 100644
index 0000000..042eb5d
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/build.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+}
+
+dependencies {
+ implementation project(":appsearch:appsearch")
+ // TODO(b/278583111) Swap with the core library version in which Function gets added.
+ implementation project(":core:core")
+ implementation("androidx.concurrent:concurrent-futures:1.0.0")
+ implementation('androidx.collection:collection:1.2.0')
+ implementation("com.google.android.gms:play-services-appsearch:16.0.0", {
+ exclude group: "androidx.fragment", module: "fragment"
+ exclude group: "androidx.core", module: "core"
+ })
+
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.truth)
+ androidTestImplementation(libs.junit)
+}
+
+androidx {
+ name = "AppSearch Play Services Storage"
+ publish = Publish.SNAPSHOT_AND_RELEASE
+ inceptionYear = "2023"
+ description =
+ "An implementation of AppSearchSession and GlobalSearchSession on pre-S devices using " +
+ "play-services-appsearch SDK with Gogle Play Services as storage backend."
+}
+
+android {
+ namespace "androidx.appsearch.playservicesstorage"
+}
diff --git a/appsearch/appsearch-play-services-storage/src/androidTest/AndroidManifest.xml b/appsearch/appsearch-play-services-storage/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..0cd568b
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
\ No newline at end of file
diff --git a/appsearch/appsearch-play-services-storage/src/androidTest/java/androidx/appsearch/playservicesstorage/PlayServicesStorageTest.java b/appsearch/appsearch-play-services-storage/src/androidTest/java/androidx/appsearch/playservicesstorage/PlayServicesStorageTest.java
new file mode 100644
index 0000000..0f4db6d
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/androidTest/java/androidx/appsearch/playservicesstorage/PlayServicesStorageTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appsearch.playservicesstorage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Test;
+
+public class PlayServicesStorageTest {
+ @Test
+ public void testSearchContext_databaseName() {
+ PlayServicesStorage.SearchContext searchContext =
+ new PlayServicesStorage.SearchContext.Builder(
+ ApplicationProvider.getApplicationContext(),
+ /*databaseName=*/"dbName").build();
+
+ assertThat(searchContext.getDatabaseName()).isEqualTo("dbName");
+ }
+
+ @Test
+ public void testSearchContext_withInvalidDatabaseName() {
+ // Test special character can present in database name. When a special character is banned
+ // in database name, add checker in SearchContext.Builder and reflect it in java doc.
+
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> new PlayServicesStorage.SearchContext.Builder(
+ ApplicationProvider.getApplicationContext(),
+ "testDatabaseNameEndWith/").build());
+ assertThat(e).hasMessageThat().isEqualTo("Database name cannot contain '/'");
+ e = assertThrows(IllegalArgumentException.class,
+ () -> new PlayServicesStorage.SearchContext.Builder(
+ ApplicationProvider.getApplicationContext(),
+ "/testDatabaseNameStartWith").build());
+ assertThat(e).hasMessageThat().isEqualTo("Database name cannot contain '/'");
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/AndroidManifest.xml b/appsearch/appsearch-play-services-storage/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8a693a4
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest />
\ No newline at end of file
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
new file mode 100644
index 0000000..2441e57d
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage;
+
+import androidx.annotation.NonNull;
+import androidx.appsearch.app.Features;
+
+/**
+ * An implementation of {@link Features}. Feature availability is dependent on Android API
+ * level and GMSCore AppSearch module.
+ */
+final class FeaturesImpl implements Features {
+ @Override
+ public boolean isFeatureSupported(@NonNull String feature) {
+ // TODO(b/274986359): Update based on features available in {@link Features} and those
+ // supported by play-services-appsearch.
+ switch (feature) {
+ // Android T Features
+ case Features.ADD_PERMISSIONS_AND_GET_VISIBILITY:
+ // fall through
+ case Features.GLOBAL_SEARCH_SESSION_GET_SCHEMA:
+ // fall through
+ case Features.GLOBAL_SEARCH_SESSION_GET_BY_ID:
+ // fall through
+ case Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH:
+ // fall through
+ return true; // AppSearch features in T, present in GMSCore AppSearch.
+
+ // RegisterObserver and UnregisterObserver are not yet supported by GMSCore AppSearch.
+ // TODO(b/208654892) : Update to reflect support once this feature is supported.
+ case Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK:
+ // Android U Features
+ case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
+ // TODO(b/203700301) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ // fall through
+ case Features.TOKENIZER_TYPE_RFC822:
+ // TODO(b/259294369) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ // fall through
+ case Features.NUMERIC_SEARCH:
+ // TODO(b/259744228) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ // fall through
+ case SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
+ // TODO(b/261474063) : Update to reflect support in Android U+ once advanced
+ // ranking becomes available.
+ // fall through
+ case Features.JOIN_SPEC_AND_QUALIFIED_ID:
+ // TODO(b/256022027) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ // fall through
+ case Features.VERBATIM_SEARCH:
+ // TODO(b/204333391) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ // fall through
+ case Features.LIST_FILTER_QUERY_LANGUAGE:
+ // TODO(b/208654892) : Update to reflect support in Android U+ once this feature is
+ // synced over into service-appsearch.
+ return false;
+ default:
+ return false; // AppSearch features in U+, absent in GMSCore AppSearch.
+ }
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java
new file mode 100644
index 0000000..dc693e8
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchBatchResult;
+import androidx.appsearch.app.Features;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetByDocumentIdRequest;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.GlobalSearchSession;
+import androidx.appsearch.app.ReportSystemUsageRequest;
+import androidx.appsearch.app.SearchResults;
+import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.observer.ObserverCallback;
+import androidx.appsearch.observer.ObserverSpec;
+import androidx.appsearch.playservicesstorage.converter.AppSearchResultToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.GenericDocumentToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.GetSchemaResponseToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.RequestToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.SearchSpecToGmsConverter;
+import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
+import androidx.core.util.Preconditions;
+
+import com.google.android.gms.appsearch.GlobalSearchClient;
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.Tasks;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An implementation of {@link GlobalSearchSession} which proxies to a Google Play Service's
+ * {@link GlobalSearchClient}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class GlobalSearchSessionImpl implements GlobalSearchSession {
+
+ private final GlobalSearchClient mGmsClient;
+ private final Features mFeatures;
+
+ GlobalSearchSessionImpl(
+ @NonNull GlobalSearchClient gmsClient,
+ @NonNull Features features) {
+ mGmsClient = Preconditions.checkNotNull(gmsClient);
+ mFeatures = Preconditions.checkNotNull(features);
+ }
+ @NonNull
+ @Override
+ public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
+ @NonNull String packageName, @NonNull String databaseName,
+ @NonNull GetByDocumentIdRequest request) {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getByDocumentId(packageName, databaseName,
+ RequestToGmsConverter.toGmsGetByDocumentIdRequest(request)),
+ result -> AppSearchResultToGmsConverter.gmsAppSearchBatchResultToJetpack(
+ result, GenericDocumentToGmsConverter::toJetpackGenericDocument));
+ }
+
+ @NonNull
+ @Override
+ public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
+ com.google.android.gms.appsearch.SearchResults searchResults =
+ mGmsClient.search(queryExpression,
+ SearchSpecToGmsConverter.toGmsSearchSpec(searchSpec));
+ return new SearchResultsImpl(searchResults, searchSpec);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> reportSystemUsageAsync(
+ @NonNull ReportSystemUsageRequest request) {
+ Task<Void> flushTask = Tasks.forResult(null);
+ return AppSearchTaskFutures.toListenableFuture(flushTask, /* valueMapper= */ i-> i);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<GetSchemaResponse> getSchemaAsync(@NonNull String packageName,
+ @NonNull String databaseName) {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getSchema(packageName, databaseName),
+ GetSchemaResponseToGmsConverter::toJetpackGetSchemaResponse);
+ }
+
+ @NonNull
+ @Override
+ public Features getFeatures() {
+ return mFeatures;
+ }
+
+ @Override
+ public void registerObserverCallback(@NonNull String targetPackageName,
+ @NonNull ObserverSpec spec, @NonNull Executor executor,
+ @NonNull ObserverCallback observer) throws AppSearchException {
+ // TODO(b/274986359): Implement registerObserverCallback for PlayServicesStorage.
+ throw new UnsupportedOperationException(
+ "registerObserverCallback is not yet supported on this AppSearch implementation.");
+ }
+
+ @Override
+ public void unregisterObserverCallback(@NonNull String targetPackageName,
+ @NonNull ObserverCallback observer) throws AppSearchException {
+ // TODO(b/274986359): Implement unregisterObserverCallback for PlayServicesStorage.
+ throw new UnsupportedOperationException(
+ "unregisterObserverCallback is not yet supported on this "
+ + "AppSearch implementation.");
+ }
+
+ @Override
+ public void close() {
+ mGmsClient.close();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java
new file mode 100644
index 0000000..eab92f9
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GlobalSearchSession;
+import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
+import androidx.core.util.Preconditions;
+
+import com.google.android.gms.appsearch.AppSearch;
+import com.google.android.gms.appsearch.AppSearchClient;
+import com.google.android.gms.appsearch.GlobalSearchClient;
+import com.google.android.gms.tasks.Task;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An AppSearch storage system which stores data in the central AppSearch service in Google
+ * Play Services.
+ */
+public final class PlayServicesStorage {
+
+ private PlayServicesStorage() {
+ }
+
+ /** Contains information about how to create the search session. */
+ public static final class SearchContext {
+ final Context mContext;
+ final String mDatabaseName;
+
+ SearchContext(@NonNull Context context, @NonNull String databaseName) {
+ mContext = Preconditions.checkNotNull(context);
+ mDatabaseName = Preconditions.checkNotNull(databaseName);
+ }
+
+ /**
+ * Returns the name of the database to create or open.
+ */
+ @NonNull
+ public String getDatabaseName() {
+ return mDatabaseName;
+ }
+
+ /** Builder for {@link SearchContext} objects. */
+ public static final class Builder {
+ private final Context mContext;
+ private final String mDatabaseName;
+
+ /**
+ * Creates a {@link SearchContext.Builder} instance.
+ *
+ * <p>{@link AppSearchSession} will create or open a database under the given name.
+ *
+ * <p>Databases with different names are fully separate with distinct schema types,
+ * namespaces, and documents.
+ *
+ * <p>The database name cannot contain {@code '/'}.
+ *
+ * <p>The database name will be visible to all system UI or third-party applications
+ * that have been granted access to any of the database's documents (for example,
+ * using {@link
+ * androidx.appsearch.app.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}).
+ *
+ * @param databaseName The name of the database.
+ * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
+ */
+ public Builder(@NonNull Context context, @NonNull String databaseName) {
+ mContext = Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(databaseName);
+ if (databaseName.contains("/")) {
+ throw new IllegalArgumentException("Database name cannot contain '/'");
+ }
+ mDatabaseName = databaseName;
+ }
+
+ /** Builds a {@link SearchContext} instance. */
+ @NonNull
+ public SearchContext build() {
+ return new SearchContext(mContext, mDatabaseName);
+ }
+ }
+ }
+
+ /** Contains information relevant to creating a global search session. */
+ public static final class GlobalSearchContext {
+ final Context mContext;
+
+ GlobalSearchContext(@NonNull Context context) {
+ mContext = Preconditions.checkNotNull(context);
+ }
+
+ /** Builder for {@link GlobalSearchContext} objects. */
+ public static final class Builder {
+ private final Context mContext;
+
+ public Builder(@NonNull Context context) {
+ mContext = Preconditions.checkNotNull(context);
+ }
+
+ /** Builds a {@link GlobalSearchContext} instance. */
+ @NonNull
+ public GlobalSearchContext build() {
+ return new GlobalSearchContext(mContext);
+ }
+ }
+ }
+
+ /**
+ * Opens a new {@link AppSearchSession} on this storage.
+ *
+ * @param context The {@link SearchContext} contains all information to create a new
+ * {@link AppSearchSession}
+ */
+ @NonNull
+ public static ListenableFuture<AppSearchSession> createSearchSessionAsync(
+ @NonNull SearchContext context) {
+ Preconditions.checkNotNull(context);
+ Task<AppSearchClient> appSearchClientTask = AppSearch
+ .createAppSearchClient(context.mContext);
+ return AppSearchTaskFutures.toListenableFuture(
+ appSearchClientTask,
+ task -> new SearchSessionImpl(task, new FeaturesImpl(), context.mDatabaseName));
+ }
+
+ /**
+ * Opens a new {@link GlobalSearchSession} on this storage.
+ *
+ * @param context The {@link GlobalSearchContext} contains all information to create a new
+ * {@link GlobalSearchSession}
+ */
+ @NonNull
+ public static ListenableFuture<GlobalSearchSession> createGlobalSearchSessionAsync(
+ @NonNull GlobalSearchContext context) {
+ Preconditions.checkNotNull(context);
+ Task<GlobalSearchClient> globalSearchClientTask = AppSearch
+ .createGlobalSearchClient(context.mContext);
+ return AppSearchTaskFutures.toListenableFuture(
+ globalSearchClientTask,
+ task -> new GlobalSearchSessionImpl(task, new FeaturesImpl()));
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchResultsImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchResultsImpl.java
new file mode 100644
index 0000000..ba23844
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchResultsImpl.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.SearchResult;
+import androidx.appsearch.app.SearchResults;
+import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.playservicesstorage.converter.SearchResultToGmsConverter;
+import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.List;
+
+/**
+ * GooglePlayService's implementation of {@link SearchResults} which proxies to the
+ * GooglePlayService's {@link com.google.android.gms.appsearch.SearchResults}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class SearchResultsImpl implements SearchResults {
+ private final com.google.android.gms.appsearch.SearchResults mGmsResults;
+
+ SearchResultsImpl(
+ @NonNull com.google.android.gms.appsearch.SearchResults gmsResults,
+ @NonNull SearchSpec searchSpec) {
+ mGmsResults = Preconditions.checkNotNull(gmsResults);
+ }
+
+ @Override
+ @NonNull
+ public ListenableFuture<List<SearchResult>> getNextPageAsync() {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsResults.getNextPage(),
+ SearchResultToGmsConverter::toJetpackSearchResultList
+ );
+ }
+
+ @Override
+ public void close() {
+ mGmsResults.close();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java
new file mode 100644
index 0000000..e34c08c
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchBatchResult;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.Features;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetByDocumentIdRequest;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.PutDocumentsRequest;
+import androidx.appsearch.app.RemoveByDocumentIdRequest;
+import androidx.appsearch.app.ReportUsageRequest;
+import androidx.appsearch.app.SearchResults;
+import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.app.SearchSuggestionResult;
+import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.appsearch.app.SetSchemaRequest;
+import androidx.appsearch.app.SetSchemaResponse;
+import androidx.appsearch.app.StorageInfo;
+import androidx.appsearch.playservicesstorage.converter.AppSearchResultToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.GenericDocumentToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.GetSchemaResponseToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.RequestToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.ResponseToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.SearchSpecToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.SetSchemaRequestToGmsConverter;
+import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
+import androidx.core.util.Preconditions;
+
+import com.google.android.gms.appsearch.AppSearchClient;
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.Tasks;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.Closeable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An implementation of {@link AppSearchSession} which proxies to a Google Play Service's
+ * {@link AppSearchClient}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class SearchSessionImpl implements AppSearchSession, Closeable {
+ private final AppSearchClient mGmsClient;
+ private final Features mFeatures;
+ private final String mDatabaseName;
+
+
+ SearchSessionImpl(
+ @NonNull AppSearchClient gmsClient,
+ @NonNull Features features,
+ @NonNull String databaseName) {
+ mGmsClient = Preconditions.checkNotNull(gmsClient);
+ mFeatures = Preconditions.checkNotNull(features);
+ mDatabaseName = Preconditions.checkNotNull(databaseName);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<SetSchemaResponse> setSchemaAsync(@NonNull SetSchemaRequest request) {
+ Preconditions.checkNotNull(request);
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.setSchema(
+ SetSchemaRequestToGmsConverter.toGmsSetSchemaRequest(
+ request),
+ mDatabaseName),
+ SetSchemaRequestToGmsConverter::toJetpackSetSchemaResponse);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<GetSchemaResponse> getSchemaAsync() {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getSchema(mDatabaseName),
+ GetSchemaResponseToGmsConverter::toJetpackGetSchemaResponse);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Set<String>> getNamespacesAsync() {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getNamespaces(mDatabaseName),
+ /* valueMapper= */ i -> i);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<AppSearchBatchResult<String, Void>> putAsync(
+ @NonNull PutDocumentsRequest request) {
+ Preconditions.checkNotNull(request);
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.put(
+ RequestToGmsConverter.toGmsPutDocumentsRequest(request),
+ mDatabaseName),
+ result -> AppSearchResultToGmsConverter.gmsAppSearchBatchResultToJetpack(
+ result, /* valueMapper= */ i -> i));
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
+ @NonNull GetByDocumentIdRequest request) {
+ Preconditions.checkNotNull(request);
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getByDocumentId(
+ RequestToGmsConverter.toGmsGetByDocumentIdRequest(request),
+ mDatabaseName),
+ result -> AppSearchResultToGmsConverter.gmsAppSearchBatchResultToJetpack(
+ result, GenericDocumentToGmsConverter::toJetpackGenericDocument));
+ }
+
+ @NonNull
+ @Override
+ public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
+ Preconditions.checkNotNull(queryExpression);
+ Preconditions.checkNotNull(searchSpec);
+ com.google.android.gms.appsearch.SearchResults gmsSearchResults =
+ mGmsClient.search(
+ queryExpression,
+ SearchSpecToGmsConverter.toGmsSearchSpec(searchSpec),
+ mDatabaseName);
+ return new SearchResultsImpl(gmsSearchResults, searchSpec);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<List<SearchSuggestionResult>> searchSuggestionAsync(
+ @NonNull String suggestionQueryExpression,
+ @NonNull SearchSuggestionSpec searchSuggestionSpec) {
+ // TODO(b/274986359): Implement searchSuggestionAsync for PlayServicesStorage.
+ throw new UnsupportedOperationException(
+ "Search Suggestion is not yet supported on this AppSearch implementation.");
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> reportUsageAsync(@NonNull ReportUsageRequest request) {
+ Preconditions.checkNotNull(request);
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.reportUsage(
+ RequestToGmsConverter.toGmsReportUsageRequest(request),
+ mDatabaseName),
+ /* valueMapper= */ i -> i);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<AppSearchBatchResult<String, Void>> removeAsync(
+ @NonNull RemoveByDocumentIdRequest request) {
+ Preconditions.checkNotNull(request);
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.remove(
+ RequestToGmsConverter
+ .toGmsRemoveByDocumentIdRequest(request),
+ mDatabaseName),
+ result -> AppSearchResultToGmsConverter.gmsAppSearchBatchResultToJetpack(
+ result, /* valueMapper= */ i -> i));
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> removeAsync(@NonNull String queryExpression,
+ @NonNull SearchSpec searchSpec) {
+ Preconditions.checkNotNull(queryExpression);
+ Preconditions.checkNotNull(searchSpec);
+ return AppSearchTaskFutures.toListenableFuture(mGmsClient.remove(
+ queryExpression,
+ SearchSpecToGmsConverter.toGmsSearchSpec(searchSpec),
+ mDatabaseName),
+ /* valueMapper= */ i -> i);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<StorageInfo> getStorageInfoAsync() {
+ return AppSearchTaskFutures.toListenableFuture(
+ mGmsClient.getStorageInfo(mDatabaseName),
+ ResponseToGmsConverter::toJetpackStorageInfo);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> requestFlushAsync() {
+ Task<Void> flushTask = Tasks.forResult(null);
+ return AppSearchTaskFutures.toListenableFuture(flushTask, /* valueMapper= */ i-> i);
+ }
+
+ @NonNull
+ @Override
+ public Features getFeatures() {
+ return mFeatures;
+ }
+
+ @Override
+ public void close() {
+ mGmsClient.close();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/AppSearchResultToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/AppSearchResultToGmsConverter.java
new file mode 100644
index 0000000..73aea40
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/AppSearchResultToGmsConverter.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchBatchResult;
+import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.exceptions.AppSearchException;
+import androidx.concurrent.futures.ResolvableFuture;
+import androidx.core.util.Function;
+import androidx.core.util.Preconditions;
+
+import java.util.Map;
+
+/**
+ * Translates {@link androidx.appsearch.app.AppSearchResult} and
+ * {@link androidx.appsearch.app.AppSearchBatchResult} to Gms versions.
+ *
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class AppSearchResultToGmsConverter {
+ private AppSearchResultToGmsConverter() {
+ }
+
+ /**
+ * Converts an {@link com.google.android.gms.appsearch.AppSearchResult} into a jetpack
+ * {@link androidx.appsearch.app.AppSearchResult}.
+ */
+ @NonNull
+ public static <GmsType, JetpackType> AppSearchResult<JetpackType>
+ gmsAppSearchResultToJetpack(
+ @NonNull com.google.android.gms.appsearch.AppSearchResult<GmsType> gmsResult,
+ @NonNull Function<GmsType, JetpackType> valueMapper) {
+ Preconditions.checkNotNull(gmsResult);
+ if (gmsResult.isSuccess()) {
+ try {
+ JetpackType jetpackType = valueMapper.apply(gmsResult.getResultValue());
+ return AppSearchResult.newSuccessfulResult(jetpackType);
+ } catch (Throwable t) {
+ return AppSearchResult.throwableToFailedResult(t);
+ }
+ }
+ return AppSearchResult.newFailedResult(
+ gmsResult.getResultCode(),
+ gmsResult.getErrorMessage());
+ }
+
+ /**
+ * Uses the given {@link com.google.android.gms.appsearch.AppSearchResult} to populate the given
+ * {@link ResolvableFuture}, transforming it using {@code valueMapper}.
+ */
+ public static <GmsType, JetpackType> void
+ gmsAppSearchResultToFuture(
+ @NonNull com.google.android.gms.appsearch.AppSearchResult<GmsType>
+ gmsResult,
+ @NonNull ResolvableFuture<JetpackType> future,
+ @NonNull Function<GmsType, JetpackType> valueMapper) {
+ Preconditions.checkNotNull(gmsResult);
+ Preconditions.checkNotNull(future);
+ if (gmsResult.isSuccess()) {
+ try {
+ JetpackType jetpackType = valueMapper.apply(gmsResult.getResultValue());
+ future.set(jetpackType);
+ } catch (Throwable t) {
+ future.setException(t);
+ }
+ } else {
+ future.setException(new AppSearchException(
+ gmsResult.getResultCode(), gmsResult.getErrorMessage()));
+ }
+ }
+
+ /**
+ * Uses the given {@link com.google.android.gms.appsearch.AppSearchResult}
+ * to populate the given {@link ResolvableFuture}.
+ */
+ public static <T> void gmsAppSearchResultToFuture(
+ @NonNull com.google.android.gms.appsearch.AppSearchResult<T> gmsResult,
+ @NonNull ResolvableFuture<T> future) {
+ gmsAppSearchResultToFuture(gmsResult,
+ future,
+ /* valueMapper= */ i -> i);
+ }
+
+ /**
+ * Converts the given Gms
+ * {@link com.google.android.gms.appsearch.AppSearchBatchResult} to a Jetpack
+ * {@link AppSearchBatchResult}.
+ *
+ * <p>Each value is translated using the provided {@code valueMapper} function.
+ */
+ @NonNull
+ public static <K, GmsValue, JetpackValue> AppSearchBatchResult<K,
+ JetpackValue> gmsAppSearchBatchResultToJetpack(
+ @NonNull com.google.android.gms.appsearch.AppSearchBatchResult<K,
+ GmsValue> gmsResult,
+ @NonNull Function<GmsValue, JetpackValue> valueMapper) {
+ Preconditions.checkNotNull(gmsResult);
+ Preconditions.checkNotNull(valueMapper);
+ AppSearchBatchResult.Builder<K, JetpackValue> jetpackResult =
+ new AppSearchBatchResult.Builder<>();
+ for (Map.Entry<K, GmsValue> success :
+ gmsResult.getSuccesses().entrySet()) {
+ try {
+ JetpackValue jetpackValue = valueMapper.apply(success.getValue());
+ jetpackResult.setSuccess(success.getKey(), jetpackValue);
+ } catch (Throwable t) {
+ jetpackResult.setResult(
+ success.getKey(), AppSearchResult.throwableToFailedResult(t));
+ }
+ }
+ for (Map.Entry<K,
+ com.google.android.gms.appsearch.AppSearchResult<GmsValue>> failure :
+ gmsResult.getFailures().entrySet()) {
+ jetpackResult.setFailure(
+ failure.getKey(),
+ failure.getValue().getResultCode(),
+ failure.getValue().getErrorMessage());
+ }
+ return jetpackResult.build();
+ }
+}
+
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java
new file mode 100644
index 0000000..2b78fbf
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.core.util.Preconditions;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link GenericDocument}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class GenericDocumentToGmsConverter {
+ private GenericDocumentToGmsConverter() {
+ }
+
+ /**
+ * Translates a jetpack {@link androidx.appsearch.app.GenericDocument} into a Gms
+ * {@link com.google.android.gms.appsearch.GenericDocument}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.GenericDocument toGmsGenericDocument(
+ @NonNull GenericDocument jetpackDocument) {
+ Preconditions.checkNotNull(jetpackDocument);
+ com.google.android.gms.appsearch.GenericDocument.Builder<
+ com.google.android.gms.appsearch.GenericDocument.Builder<?>>
+ gmsBuilder =
+ new com.google.android.gms.appsearch.GenericDocument.Builder<>(
+ jetpackDocument.getNamespace(),
+ jetpackDocument.getId(),
+ jetpackDocument.getSchemaType());
+ gmsBuilder
+ .setScore(jetpackDocument.getScore())
+ .setTtlMillis(jetpackDocument.getTtlMillis())
+ .setCreationTimestampMillis(jetpackDocument.getCreationTimestampMillis());
+ for (String propertyName : jetpackDocument.getPropertyNames()) {
+ Object property = jetpackDocument.getProperty(propertyName);
+ if (property instanceof String[]) {
+ gmsBuilder.setPropertyString(propertyName, (String[]) property);
+ } else if (property instanceof long[]) {
+ gmsBuilder.setPropertyLong(propertyName, (long[]) property);
+ } else if (property instanceof double[]) {
+ gmsBuilder.setPropertyDouble(propertyName, (double[]) property);
+ } else if (property instanceof boolean[]) {
+ gmsBuilder.setPropertyBoolean(propertyName, (boolean[]) property);
+ } else if (property instanceof byte[][]) {
+ byte[][] byteValues = (byte[][]) property;
+ gmsBuilder.setPropertyBytes(propertyName, byteValues);
+ } else if (property instanceof GenericDocument[]) {
+ GenericDocument[] documentValues = (GenericDocument[]) property;
+ com.google.android.gms.appsearch.GenericDocument[] gmsSubDocuments =
+ new com.google.android.gms.appsearch.GenericDocument[documentValues.length];
+ for (int j = 0; j < documentValues.length; j++) {
+ gmsSubDocuments[j] = toGmsGenericDocument(documentValues[j]);
+ }
+ gmsBuilder.setPropertyDocument(propertyName,
+ gmsSubDocuments);
+ } else {
+ throw new IllegalStateException(
+ String.format("Property \"%s\" has unsupported value type %s",
+ propertyName,
+ property.getClass().toString()));
+ }
+ }
+ return gmsBuilder.build();
+ }
+
+ /**
+ * Translates a Gms {@link com.google.android.gms.appsearch.GenericDocument}
+ * into a jetpack {@link androidx.appsearch.app.GenericDocument}.
+ */
+ @NonNull
+ public static GenericDocument toJetpackGenericDocument(
+ @NonNull com.google.android.gms.appsearch.GenericDocument
+ gmsDocument) {
+ Preconditions.checkNotNull(gmsDocument);
+ GenericDocument.Builder<GenericDocument.Builder<?>> jetpackBuilder =
+ new GenericDocument.Builder<>(
+ gmsDocument.getNamespace(),
+ gmsDocument.getId(),
+ gmsDocument.getSchemaType());
+ jetpackBuilder
+ .setScore(gmsDocument.getScore())
+ .setTtlMillis(gmsDocument.getTtlMillis())
+ .setCreationTimestampMillis(gmsDocument
+ .getCreationTimestampMillis());
+ for (String propertyName : gmsDocument.getPropertyNames()) {
+ Object property = gmsDocument.getProperty(propertyName);
+ if (property instanceof String[]) {
+ jetpackBuilder.setPropertyString(propertyName, (String[]) property);
+ } else if (property instanceof long[]) {
+ jetpackBuilder.setPropertyLong(propertyName, (long[]) property);
+ } else if (property instanceof double[]) {
+ jetpackBuilder.setPropertyDouble(propertyName, (double[]) property);
+ } else if (property instanceof boolean[]) {
+ jetpackBuilder.setPropertyBoolean(propertyName, (boolean[]) property);
+ } else if (property instanceof byte[][]) {
+ jetpackBuilder.setPropertyBytes(propertyName, (byte[][]) property);
+ } else if (property instanceof com.google.android.gms.appsearch.GenericDocument[]) {
+ com.google.android.gms.appsearch.GenericDocument[] documentValues =
+ (com.google.android.gms.appsearch.GenericDocument[]) property;
+ GenericDocument[] jetpackSubDocuments = new GenericDocument[documentValues.length];
+ for (int j = 0; j < documentValues.length; j++) {
+ jetpackSubDocuments[j] = toJetpackGenericDocument(documentValues[j]);
+ }
+ jetpackBuilder.setPropertyDocument(propertyName, jetpackSubDocuments);
+ } else {
+ throw new IllegalStateException(
+ String.format("Property \"%s\" has unsupported value type %s", propertyName,
+ property.getClass().toString()));
+ }
+ }
+ return jetpackBuilder.build();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java
new file mode 100644
index 0000000..bacb0b8
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.collection.ArraySet;
+import androidx.core.util.Preconditions;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link GetSchemaResponse}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class GetSchemaResponseToGmsConverter {
+ private GetSchemaResponseToGmsConverter() {
+ }
+
+ /**
+ * Translates a Gms {@link com.google.android.gms.appsearch.GetSchemaResponse}
+ * into a jetpack
+ * {@link GetSchemaResponse}.
+ */
+ @NonNull
+ public static GetSchemaResponse toJetpackGetSchemaResponse(
+ @NonNull com.google.android.gms.appsearch.GetSchemaResponse
+ gmsResponse) {
+ Preconditions.checkNotNull(gmsResponse);
+ GetSchemaResponse.Builder jetpackBuilder;
+ jetpackBuilder = new GetSchemaResponse.Builder();
+ for (com.google.android.gms.appsearch.AppSearchSchema gmsSchema :
+ gmsResponse.getSchemas()) {
+ jetpackBuilder.addSchema(SchemaToGmsConverter.toJetpackSchema(
+ gmsSchema));
+ }
+ jetpackBuilder.setVersion(gmsResponse.getVersion());
+ // Convert schemas not displayed by system
+ for (String schemaTypeNotDisplayedBySystem :
+ gmsResponse.getSchemaTypesNotDisplayedBySystem()) {
+ jetpackBuilder.addSchemaTypeNotDisplayedBySystem(schemaTypeNotDisplayedBySystem);
+ }
+ // Convert schemas visible to packages
+ convertSchemasVisibleToPackages(gmsResponse, jetpackBuilder);
+ // Convert schemas visible to permissions
+ for (Map.Entry<String, Set<Set<Integer>>> entry :
+ gmsResponse.getRequiredPermissionsForSchemaTypeVisibility()
+ .entrySet()) {
+ jetpackBuilder.setRequiredPermissionsForSchemaTypeVisibility(entry.getKey(),
+ entry.getValue());
+ }
+ return jetpackBuilder.build();
+ }
+
+ /**
+ * Adds package visibilities in a Gms
+ * {@link com.google.android.gms.appsearch.GetSchemaResponse} into
+ * the given jetpack {@link GetSchemaResponse}.
+ */
+ private static void convertSchemasVisibleToPackages(
+ @NonNull com.google.android.gms.appsearch.GetSchemaResponse gmsResponse,
+ @NonNull GetSchemaResponse.Builder jetpackBuilder) {
+ Map<String, Set<com.google.android.gms.appsearch.PackageIdentifier>>
+ schemaTypesVisibleToPackages =
+ gmsResponse.getSchemaTypesVisibleToPackages();
+ if (schemaTypesVisibleToPackages != null) {
+ for (Map.Entry<String, Set<com.google.android.gms.appsearch.PackageIdentifier>> entry
+ : schemaTypesVisibleToPackages.entrySet()) {
+ Set<PackageIdentifier> jetpackPackageIdentifiers =
+ new ArraySet<>(entry.getValue().size());
+ for (com.google.android.gms.appsearch.PackageIdentifier frameworkPackageIdentifier
+ : entry.getValue()) {
+ jetpackPackageIdentifiers.add(new PackageIdentifier(
+ frameworkPackageIdentifier.getPackageName(),
+ frameworkPackageIdentifier.getSha256Certificate()));
+ }
+ jetpackBuilder.setSchemaTypeVisibleToPackages(
+ entry.getKey(), jetpackPackageIdentifiers);
+ }
+ }
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java
new file mode 100644
index 0000000..34cebb8
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetByDocumentIdRequest;
+import androidx.appsearch.app.PutDocumentsRequest;
+import androidx.appsearch.app.RemoveByDocumentIdRequest;
+import androidx.appsearch.app.ReportUsageRequest;
+import androidx.core.util.Preconditions;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates between Gms and Jetpack versions of requests.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class RequestToGmsConverter {
+ private RequestToGmsConverter() {
+ }
+
+ /**
+ * Translates a jetpack {@link PutDocumentsRequest} into a Gms
+ * {@link com.google.android.gms.appsearch.PutDocumentsRequest}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.PutDocumentsRequest toGmsPutDocumentsRequest(
+ @NonNull PutDocumentsRequest jetpackRequest) {
+ Preconditions.checkNotNull(jetpackRequest);
+ com.google.android.gms.appsearch.PutDocumentsRequest.Builder gmsBuilder =
+ new com.google.android.gms.appsearch.PutDocumentsRequest.Builder();
+ for (GenericDocument jetpackDocument : jetpackRequest.getGenericDocuments()) {
+ gmsBuilder.addGenericDocuments(
+ GenericDocumentToGmsConverter.toGmsGenericDocument(
+ jetpackDocument));
+ }
+ return gmsBuilder.build();
+ }
+
+ /**
+ * Translates a jetpack {@link GetByDocumentIdRequest} into a Gms
+ * {@link com.google.android.gms.appsearch.GetByDocumentIdRequest}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.GetByDocumentIdRequest
+ toGmsGetByDocumentIdRequest(@NonNull GetByDocumentIdRequest jetpackRequest) {
+ Preconditions.checkNotNull(jetpackRequest);
+ com.google.android.gms.appsearch.GetByDocumentIdRequest.Builder gmsBuilder =
+ new com.google.android.gms.appsearch.GetByDocumentIdRequest.Builder(
+ jetpackRequest.getNamespace())
+ .addIds(jetpackRequest.getIds());
+ for (Map.Entry<String, List<String>> projection :
+ jetpackRequest.getProjectionsInternal().entrySet()) {
+ gmsBuilder.addProjection(projection.getKey(), projection.getValue());
+ }
+ return gmsBuilder.build();
+ }
+
+ /**
+ * Translates a jetpack {@link RemoveByDocumentIdRequest} into a Gms
+ * {@link com.google.android.gms.appsearch.RemoveByDocumentIdRequest}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.RemoveByDocumentIdRequest
+ toGmsRemoveByDocumentIdRequest(@NonNull RemoveByDocumentIdRequest jetpackRequest) {
+ Preconditions.checkNotNull(jetpackRequest);
+ return new com.google.android.gms.appsearch.RemoveByDocumentIdRequest.Builder(
+ jetpackRequest.getNamespace())
+ .addIds(jetpackRequest.getIds())
+ .build();
+ }
+
+ /**
+ * Translates a jetpack {@link androidx.appsearch.app.ReportUsageRequest} into a
+ * Gms
+ * {@link com.google.android.gms.appsearch.ReportUsageRequest}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.ReportUsageRequest
+ toGmsReportUsageRequest(@NonNull ReportUsageRequest jetpackRequest) {
+ Preconditions.checkNotNull(jetpackRequest);
+ return new com.google.android.gms.appsearch.ReportUsageRequest.Builder(
+ jetpackRequest.getNamespace(), jetpackRequest.getDocumentId())
+ .setUsageTimestampMillis(jetpackRequest.getUsageTimestampMillis())
+ .build();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/ResponseToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/ResponseToGmsConverter.java
new file mode 100644
index 0000000..3aa15b2
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/ResponseToGmsConverter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.StorageInfo;
+import androidx.core.util.Preconditions;
+
+/**
+ * Translates between Gms and Jetpack versions of responses.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ResponseToGmsConverter {
+ private ResponseToGmsConverter() {
+ }
+
+ /**
+ * Translates a Gms {@link com.google.android.gms.appsearch.StorageInfo} into
+ * a jetpack {@link StorageInfo}.
+ */
+ @NonNull
+ public static StorageInfo toJetpackStorageInfo(
+ @NonNull com.google.android.gms.appsearch.StorageInfo gmsStorageInfo) {
+ Preconditions.checkNotNull(gmsStorageInfo);
+ return new StorageInfo.Builder()
+ .setAliveNamespacesCount(gmsStorageInfo.getAliveNamespacesCount())
+ .setAliveDocumentsCount(gmsStorageInfo.getAliveDocumentsCount())
+ .setSizeBytes(gmsStorageInfo.getSizeBytes())
+ .build();
+
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java
new file mode 100644
index 0000000..3c507a5
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.core.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Translates a jetpack {@link androidx.appsearch.app.AppSearchSchema} into a Gms
+ * {@link com.google.android.gms.appsearch.AppSearchSchema}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SchemaToGmsConverter {
+ private SchemaToGmsConverter() {
+ }
+
+ /**
+ * Translates a jetpack {@link androidx.appsearch.app.AppSearchSchema} into a Gms
+ * {@link com.google.android.gms.appsearch.AppSearchSchema}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.AppSearchSchema toGmsSchema(
+ @NonNull AppSearchSchema jetpackSchema) {
+ Preconditions.checkNotNull(jetpackSchema);
+ com.google.android.gms.appsearch.AppSearchSchema.Builder gmsBuilder =
+ new com.google.android.gms.appsearch.AppSearchSchema
+ .Builder(jetpackSchema.getSchemaType());
+ List<AppSearchSchema.PropertyConfig> properties = jetpackSchema.getProperties();
+ for (int i = 0; i < properties.size(); i++) {
+ com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig gmsProperty =
+ toGmsProperty(properties.get(i));
+ gmsBuilder.addProperty(gmsProperty);
+ }
+ return gmsBuilder.build();
+ }
+
+ /**
+ * Translates a Gms {@link com.google.android.gms.appsearch.AppSearchSchema}
+ * to a jetpack
+ * {@link androidx.appsearch.app.AppSearchSchema}.
+ */
+ @NonNull
+ public static AppSearchSchema toJetpackSchema(
+ @NonNull com.google.android.gms.appsearch.AppSearchSchema gmsSchema) {
+ Preconditions.checkNotNull(gmsSchema);
+ AppSearchSchema.Builder jetpackBuilder =
+ new AppSearchSchema.Builder(gmsSchema.getSchemaType());
+ List<com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig> properties =
+ gmsSchema.getProperties();
+ for (int i = 0; i < properties.size(); i++) {
+ AppSearchSchema.PropertyConfig jetpackProperty = toJetpackProperty(properties.get(i));
+ jetpackBuilder.addProperty(jetpackProperty);
+ }
+ return jetpackBuilder.build();
+ }
+
+ @NonNull
+ private static com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig toGmsProperty(
+ @NonNull AppSearchSchema.PropertyConfig jetpackProperty) {
+ Preconditions.checkNotNull(jetpackProperty);
+ if (jetpackProperty instanceof AppSearchSchema.StringPropertyConfig) {
+ AppSearchSchema.StringPropertyConfig stringProperty =
+ (AppSearchSchema.StringPropertyConfig) jetpackProperty;
+ com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig.Builder
+ gmsBuilder =
+ new com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig
+ .Builder(stringProperty.getName())
+ .setCardinality(stringProperty.getCardinality())
+ .setIndexingType(stringProperty.getIndexingType())
+ .setTokenizerType(stringProperty.getTokenizerType());
+ if (stringProperty.getDeletionPropagation()) {
+ // TODO(b/268521214): Update once deletion propagation is available.
+ throw new UnsupportedOperationException("Setting deletion propagation is not "
+ + "supported on this AppSearch implementation.");
+ }
+
+ if (stringProperty.getJoinableValueType()
+ == AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
+ //TODO(b/274986359) Add GMSCore feature check for Joins once available.
+ throw new UnsupportedOperationException(
+ "StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID is not supported"
+ + " on this AppSearch implementation.");
+ }
+ return gmsBuilder.build();
+ } else if (jetpackProperty instanceof AppSearchSchema.LongPropertyConfig) {
+ AppSearchSchema.LongPropertyConfig longProperty =
+ (AppSearchSchema.LongPropertyConfig) jetpackProperty;
+ com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig.Builder
+ longPropertyBuilder =
+ new com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig
+ .Builder(jetpackProperty.getName())
+ .setCardinality(jetpackProperty.getCardinality());
+ if (longProperty.getIndexingType()
+ == AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE) {
+ //TODO(b/274986359) Add GMSCore feature check for Indexing Range once available.
+ throw new UnsupportedOperationException(
+ "LongProperty.INDEXING_TYPE_RANGE is not supported on this AppSearch "
+ + "implementation.");
+ }
+ return longPropertyBuilder.build();
+ } else if (jetpackProperty instanceof AppSearchSchema.DoublePropertyConfig) {
+ return new com.google.android.gms.appsearch.AppSearchSchema.DoublePropertyConfig
+ .Builder(
+ jetpackProperty.getName())
+ .setCardinality(jetpackProperty.getCardinality())
+ .build();
+ } else if (jetpackProperty instanceof AppSearchSchema.BooleanPropertyConfig) {
+ return new com.google.android.gms.appsearch.AppSearchSchema.BooleanPropertyConfig
+ .Builder(
+ jetpackProperty.getName())
+ .setCardinality(jetpackProperty.getCardinality())
+ .build();
+ } else if (jetpackProperty instanceof AppSearchSchema.BytesPropertyConfig) {
+ return new com.google.android.gms.appsearch.AppSearchSchema.BytesPropertyConfig
+ .Builder(
+ jetpackProperty.getName())
+ .setCardinality(jetpackProperty.getCardinality())
+ .build();
+ } else if (jetpackProperty instanceof AppSearchSchema.DocumentPropertyConfig) {
+ AppSearchSchema.DocumentPropertyConfig documentProperty =
+ (AppSearchSchema.DocumentPropertyConfig) jetpackProperty;
+ return new com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig
+ .Builder(
+ documentProperty.getName(), documentProperty.getSchemaType())
+ .setCardinality(documentProperty.getCardinality())
+ .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
+ .build();
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid dataType: " + jetpackProperty.getDataType());
+ }
+ }
+
+ @NonNull
+ private static AppSearchSchema.PropertyConfig toJetpackProperty(
+ @NonNull com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig
+ gmsProperty) {
+ Preconditions.checkNotNull(gmsProperty);
+ if (gmsProperty
+ instanceof com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig) {
+ com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig stringProperty =
+ (com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig)
+ gmsProperty;
+ return new AppSearchSchema.StringPropertyConfig.Builder(stringProperty.getName())
+ .setCardinality(stringProperty.getCardinality())
+ .setIndexingType(stringProperty.getIndexingType())
+ .setTokenizerType(stringProperty.getTokenizerType())
+ .build();
+ } else if (gmsProperty
+ instanceof com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig) {
+ return new AppSearchSchema.LongPropertyConfig.Builder(
+ gmsProperty.getName())
+ .setCardinality(gmsProperty.getCardinality())
+ .build();
+ } else if (gmsProperty
+ instanceof com.google.android.gms.appsearch.AppSearchSchema.DoublePropertyConfig) {
+ return new AppSearchSchema.DoublePropertyConfig.Builder(
+ gmsProperty.getName())
+ .setCardinality(gmsProperty.getCardinality())
+ .build();
+ } else if (gmsProperty
+ instanceof com.google.android.gms.appsearch.AppSearchSchema.BooleanPropertyConfig) {
+ return new AppSearchSchema.BooleanPropertyConfig.Builder(
+ gmsProperty.getName())
+ .setCardinality(gmsProperty.getCardinality())
+ .build();
+ } else if (gmsProperty
+ instanceof com.google.android.gms.appsearch.AppSearchSchema.BytesPropertyConfig) {
+ return new AppSearchSchema.BytesPropertyConfig.Builder(
+ gmsProperty.getName())
+ .setCardinality(gmsProperty.getCardinality())
+ .build();
+ } else if (gmsProperty
+ instanceof
+ com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig) {
+ com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig
+ documentProperty =
+ (com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig)
+ gmsProperty;
+ return new AppSearchSchema.DocumentPropertyConfig.Builder(
+ documentProperty.getName(),
+ documentProperty.getSchemaType())
+ .setCardinality(documentProperty.getCardinality())
+ .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
+ .build();
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid property type " + gmsProperty.getClass()
+ + ": " + gmsProperty);
+ }
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java
new file mode 100644
index 0000000..81a0206
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.SearchResult;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link SearchResult}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SearchResultToGmsConverter {
+ private SearchResultToGmsConverter() {
+ }
+
+ /** Translates from Gms to Jetpack versions of list of {@link SearchResult}. */
+ @NonNull
+ public static List<SearchResult> toJetpackSearchResultList(
+ @NonNull List<com.google.android.gms.appsearch.SearchResult> gmsResultList) {
+ Preconditions.checkNotNull(gmsResultList);
+ List<androidx.appsearch.app.SearchResult> jetpackResults =
+ new ArrayList<>(gmsResultList.size());
+ for (int i = 0; i < gmsResultList.size(); i++) {
+ androidx.appsearch.app.SearchResult jetpackResult =
+ toJetpackSearchResult(gmsResultList.get(i));
+ jetpackResults.add(jetpackResult);
+ }
+ return jetpackResults;
+ }
+
+ /** Translates from Gms to Jetpack versions of {@link SearchResult}. */
+ @NonNull
+ public static SearchResult toJetpackSearchResult(
+ @NonNull com.google.android.gms.appsearch.SearchResult gmsResult) {
+ Preconditions.checkNotNull(gmsResult);
+ GenericDocument document =
+ GenericDocumentToGmsConverter.toJetpackGenericDocument(
+ gmsResult.getGenericDocument());
+ SearchResult.Builder builder = new SearchResult.Builder(
+ gmsResult.getPackageName(),
+ gmsResult.getDatabaseName())
+ .setGenericDocument(document)
+ .setRankingSignal(gmsResult.getRankingSignal());
+ List<com.google.android.gms.appsearch.SearchResult.MatchInfo> gmsMatches =
+ gmsResult.getMatchInfos();
+ for (int i = 0; i < gmsMatches.size(); i++) {
+ SearchResult.MatchInfo jetpackMatchInfo = toJetpackMatchInfo(
+ gmsMatches.get(i));
+ builder.addMatchInfo(jetpackMatchInfo);
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ private static SearchResult.MatchInfo toJetpackMatchInfo(
+ @NonNull com.google.android.gms.appsearch.SearchResult.MatchInfo
+ gmsMatchInfo) {
+ Preconditions.checkNotNull(gmsMatchInfo);
+ SearchResult.MatchInfo.Builder builder = new SearchResult.MatchInfo.Builder(
+ gmsMatchInfo.getPropertyPath())
+ .setExactMatchRange(
+ new SearchResult.MatchRange(
+ gmsMatchInfo.getExactMatchRange().getStart(),
+ gmsMatchInfo.getExactMatchRange().getEnd()))
+ .setSnippetRange(
+ new SearchResult.MatchRange(
+ gmsMatchInfo.getSnippetRange().getStart(),
+ gmsMatchInfo.getSnippetRange().getEnd()));
+ builder.setSubmatchRange(
+ new SearchResult.MatchRange(
+ gmsMatchInfo.getSubmatchRange().getStart(),
+ gmsMatchInfo.getSubmatchRange().getEnd()));
+ return builder.build();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java
new file mode 100644
index 0000000..8922bd4
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.Features;
+import androidx.appsearch.app.SearchSpec;
+import androidx.core.util.Preconditions;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link SearchSpec}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SearchSpecToGmsConverter {
+ private SearchSpecToGmsConverter() {
+ }
+
+ /** Translates from Jetpack to Gms version of {@link SearchSpec}. */
+ @NonNull
+ public static com.google.android.gms.appsearch.SearchSpec toGmsSearchSpec(
+ @NonNull SearchSpec jetpackSearchSpec) {
+ Preconditions.checkNotNull(jetpackSearchSpec);
+ com.google.android.gms.appsearch.SearchSpec.Builder gmsBuilder =
+ new com.google.android.gms.appsearch.SearchSpec.Builder();
+
+ if (!jetpackSearchSpec.getAdvancedRankingExpression().isEmpty()) {
+ //TODO(b/274986359) Add GMSCore feature check for Advanced Ranking once available.
+ throw new UnsupportedOperationException(
+ Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION
+ + " is not available on this AppSearch implementation.");
+ } else {
+ gmsBuilder.setRankingStrategy(jetpackSearchSpec.getRankingStrategy());
+ }
+
+ gmsBuilder
+ .setTermMatch(jetpackSearchSpec.getTermMatch())
+ .addFilterSchemas(jetpackSearchSpec.getFilterSchemas())
+ .addFilterNamespaces(jetpackSearchSpec.getFilterNamespaces())
+ .addFilterPackageNames(jetpackSearchSpec.getFilterPackageNames())
+ .setResultCountPerPage(jetpackSearchSpec.getResultCountPerPage())
+ .setRankingStrategy(jetpackSearchSpec.getRankingStrategy())
+ .setOrder(jetpackSearchSpec.getOrder())
+ .setSnippetCount(jetpackSearchSpec.getSnippetCount())
+ .setSnippetCountPerProperty(jetpackSearchSpec.getSnippetCountPerProperty())
+ .setMaxSnippetSize(jetpackSearchSpec.getMaxSnippetSize());
+ if (jetpackSearchSpec.getResultGroupingTypeFlags() != 0) {
+ gmsBuilder.setResultGrouping(
+ jetpackSearchSpec.getResultGroupingTypeFlags(),
+ jetpackSearchSpec.getResultGroupingLimit());
+ }
+ for (Map.Entry<String, List<String>> projection :
+ jetpackSearchSpec.getProjections().entrySet()) {
+ gmsBuilder.addProjection(projection.getKey(), projection.getValue());
+ }
+
+ if (!jetpackSearchSpec.getEnabledFeatures().isEmpty()) {
+ if (jetpackSearchSpec.isNumericSearchEnabled()
+ || jetpackSearchSpec.isVerbatimSearchEnabled()
+ || jetpackSearchSpec.isListFilterQueryLanguageEnabled()) {
+ //TODO(b/274986359) Add GMSCore feature check for NUMERIC_SEARCH,
+ // VERBATIM_SEARCH and LIST_FILTER_QUERY_LANGUAGE once available.
+ throw new UnsupportedOperationException(
+ "Advanced query features (NUMERIC_SEARCH, VERBATIM_SEARCH and "
+ + "LIST_FILTER_QUERY_LANGUAGE) are not supported with this "
+ + "backend/Android API level combination.");
+ }
+ }
+
+ if (!jetpackSearchSpec.getPropertyWeights().isEmpty()) {
+ //TODO(b/274986359) Add GMSCore feature check for Property Weights once available.
+ throw new UnsupportedOperationException(
+ "Property weights are not supported with this backend/Android API level "
+ + "combination.");
+ }
+
+ if (jetpackSearchSpec.getJoinSpec() != null) {
+ //TODO(b/274986359) Add GMSCore feature check for Joins once available.
+ throw new UnsupportedOperationException("JoinSpec is not available on this "
+ + "AppSearch implementation.");
+ }
+
+ return gmsBuilder.build();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java
new file mode 100644
index 0000000..3929b54
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.Migrator;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SetSchemaRequest;
+import androidx.appsearch.app.SetSchemaResponse;
+import androidx.core.util.Preconditions;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link SetSchemaRequest}.
+
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SetSchemaRequestToGmsConverter {
+ private SetSchemaRequestToGmsConverter() {
+ }
+
+ /**
+ * Translates a jetpack {@link SetSchemaRequest} into a googleGms
+ * {@link com.google.android.gms.appsearch.SetSchemaRequest}.
+ */
+ @NonNull
+ public static com.google.android.gms.appsearch.SetSchemaRequest toGmsSetSchemaRequest(
+ @NonNull SetSchemaRequest jetpackRequest) {
+ Preconditions.checkNotNull(jetpackRequest);
+ com.google.android.gms.appsearch.SetSchemaRequest.Builder gmsBuilder =
+ new com.google.android.gms.appsearch.SetSchemaRequest.Builder();
+ for (AppSearchSchema jetpackSchema : jetpackRequest.getSchemas()) {
+ gmsBuilder.addSchemas(SchemaToGmsConverter.toGmsSchema(jetpackSchema));
+ }
+ for (String schemaNotDisplayedBySystem : jetpackRequest.getSchemasNotDisplayedBySystem()) {
+ gmsBuilder.setSchemaTypeDisplayedBySystem(
+ schemaNotDisplayedBySystem, /*displayed=*/ false);
+ }
+ for (Map.Entry<String, Set<PackageIdentifier>> jetpackSchemaVisibleToPackage :
+ jetpackRequest.getSchemasVisibleToPackagesInternal().entrySet()) {
+ for (PackageIdentifier jetpackPackageIdentifier :
+ jetpackSchemaVisibleToPackage.getValue()) {
+ gmsBuilder.setSchemaTypeVisibilityForPackage(
+ jetpackSchemaVisibleToPackage.getKey(),
+ /*visible=*/ true,
+ new com.google.android.gms.appsearch.PackageIdentifier(
+ jetpackPackageIdentifier.getPackageName(),
+ jetpackPackageIdentifier.getSha256Certificate()));
+ }
+ }
+ if (!jetpackRequest.getRequiredPermissionsForSchemaTypeVisibility().isEmpty()) {
+ for (Map.Entry<String, Set<Set<Integer>>> entry :
+ jetpackRequest.getRequiredPermissionsForSchemaTypeVisibility().entrySet()) {
+ for (Set<Integer> permissionGroup : entry.getValue()) {
+ gmsBuilder.addRequiredPermissionsForSchemaTypeVisibility(
+ entry.getKey(), permissionGroup);
+ }
+ }
+ }
+ for (Map.Entry<String, Migrator> entry : jetpackRequest.getMigrators().entrySet()) {
+ Migrator jetpackMigrator = entry.getValue();
+ com.google.android.gms.appsearch.Migrator gmsMigrator =
+ new com.google.android.gms.appsearch.Migrator() {
+ @Override
+ public boolean shouldMigrate(int currentVersion, int finalVersion) {
+ return jetpackMigrator.shouldMigrate(currentVersion, finalVersion);
+ }
+
+ @NonNull
+ @Override
+ public com.google.android.gms.appsearch.GenericDocument onUpgrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull com.google.android.gms.appsearch.GenericDocument
+ inGmsDocument) {
+ GenericDocument inJetpackDocument =
+ GenericDocumentToGmsConverter
+ .toJetpackGenericDocument(
+ inGmsDocument);
+ GenericDocument outJetpackDocument = jetpackMigrator.onUpgrade(
+ currentVersion, finalVersion, inJetpackDocument);
+ if (inJetpackDocument.equals(outJetpackDocument)) {
+ return inGmsDocument; // Same object; no conversion occurred.
+ }
+ return GenericDocumentToGmsConverter
+ .toGmsGenericDocument(
+ outJetpackDocument);
+ }
+
+ @NonNull
+ @Override
+ public com.google.android.gms.appsearch.GenericDocument onDowngrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull com.google.android.gms.appsearch.GenericDocument
+ inGmsDocument) {
+ GenericDocument inJetpackDocument =
+ GenericDocumentToGmsConverter
+ .toJetpackGenericDocument(
+ inGmsDocument);
+ GenericDocument outJetpackDocument = jetpackMigrator.onDowngrade(
+ currentVersion, finalVersion, inJetpackDocument);
+ if (inJetpackDocument.equals(outJetpackDocument)) {
+ return inGmsDocument; // Same object; no conversion occurred.
+ }
+ return GenericDocumentToGmsConverter
+ .toGmsGenericDocument(
+ outJetpackDocument);
+ }
+ };
+ gmsBuilder.setMigrator(entry.getKey(), gmsMigrator);
+ }
+ return gmsBuilder
+ .setForceOverride(jetpackRequest.isForceOverride())
+ .setVersion(jetpackRequest.getVersion())
+ .build();
+ }
+
+ /**
+ * Translates a gms
+ * {@link com.google.android.gms.appsearch.SetSchemaResponse} into a jetpack
+ * {@link SetSchemaResponse}.
+ */
+ @NonNull
+ public static SetSchemaResponse toJetpackSetSchemaResponse(
+ @NonNull com.google.android.gms.appsearch.SetSchemaResponse
+ gmsResponse) {
+ Preconditions.checkNotNull(gmsResponse);
+ SetSchemaResponse.Builder jetpackBuilder = new SetSchemaResponse.Builder()
+ .addDeletedTypes(gmsResponse.getDeletedTypes())
+ .addIncompatibleTypes(gmsResponse.getIncompatibleTypes())
+ .addMigratedTypes(gmsResponse.getMigratedTypes());
+ for (com.google.android.gms.appsearch.SetSchemaResponse.MigrationFailure migrationFailure :
+ gmsResponse.getMigrationFailures()) {
+ jetpackBuilder.addMigrationFailure(new SetSchemaResponse.MigrationFailure(
+ migrationFailure.getNamespace(),
+ migrationFailure.getDocumentId(),
+ migrationFailure.getSchemaType(),
+ AppSearchResultToGmsConverter.gmsAppSearchResultToJetpack(
+ migrationFailure.getAppSearchResult(), /* valueMapper= */ i -> i))
+ );
+ }
+ return jetpackBuilder.build();
+ }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/util/AppSearchTaskFutures.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/util/AppSearchTaskFutures.java
new file mode 100644
index 0000000..d737512a
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/util/AppSearchTaskFutures.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.playservicesstorage.util;
+
+import static androidx.appsearch.app.AppSearchResult.RESULT_NOT_FOUND;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.util.LogUtil;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.core.util.Function;
+
+import com.google.android.gms.tasks.Task;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/** Utilities for converting {@link Task} to {@link ListenableFuture}. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AppSearchTaskFutures {
+
+ private static final String TAG = "AppSearchTaskFutures";
+
+ private AppSearchTaskFutures() {}
+
+ /**
+ * Converts a {@link Task} to a {@link ListenableFuture} to more easily interact with other
+ * androidx apis.
+ * <p>Note: Calling {@link java.util.concurrent.Future#cancel(boolean)} on the returned result
+ * is a no-op since {@link Task} has no equivalent method.
+ */
+ @NonNull
+ public static <GmsType, JetpackType> ListenableFuture<JetpackType> toListenableFuture(
+ @NonNull Task<GmsType> task,
+ @NonNull Function<GmsType, JetpackType> valueMapper) {
+ return CallbackToFutureAdapter.getFuture(
+ completer -> task.addOnCompleteListener(
+ completedTask -> {
+ if (completedTask.isCanceled()) {
+ completer.setCancelled();
+ } else if (completedTask.isSuccessful()) {
+ JetpackType jetpackType = valueMapper.apply(
+ completedTask.getResult());
+ completer.set(jetpackType);
+ } else {
+ Exception exception = task.getException();
+ completer.setException(toJetpackException(exception));
+ }
+ }));
+ }
+
+ /**
+ * Converts the given Exception to AppSearchException if from PlayServicesAppSearch otherwise
+ * just returns it.
+ */
+ @NonNull
+ private static Exception toJetpackException(@NonNull Exception exception) {
+ if (exception instanceof com.google.android.gms.appsearch.exceptions.AppSearchException) {
+ com.google.android.gms.appsearch.exceptions.AppSearchException
+ gmsException =
+ (com.google.android.gms.appsearch.exceptions.AppSearchException) exception;
+ if (gmsException.getResultCode() == RESULT_NOT_FOUND && LogUtil.DEBUG) {
+ // Log for traceability. NOT_FOUND is logged at VERBOSE because this
+ // error can occur during the regular operation of the system
+ // (b/183550974). Everything else is indicative of an actual
+ // problem and is logged at WARN.
+ Log.v(TAG, "Failed to call PlayServicesAppSearch: "
+ + exception);
+ }
+
+ return new AppSearchException(
+ gmsException.getResultCode(),
+ gmsException.getMessage());
+ }
+
+ Log.w(TAG, "Failed to call PlayServicesAppSearch", exception);
+ return exception;
+ }
+}
diff --git a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchEmail.java b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchEmail.java
index 5aec156..08b1840 100644
--- a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchEmail.java
+++ b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchEmail.java
@@ -19,6 +19,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchSchema;
import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
@@ -170,6 +171,7 @@
/**
* Sets the from address of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setFrom(@NonNull String from) {
return setPropertyString(KEY_FROM, from);
@@ -178,6 +180,7 @@
/**
* Sets the destination address of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setTo(@NonNull String... to) {
return setPropertyString(KEY_TO, to);
@@ -186,6 +189,7 @@
/**
* Sets the CC list of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setCc(@NonNull String... cc) {
return setPropertyString(KEY_CC, cc);
@@ -194,6 +198,7 @@
/**
* Sets the BCC list of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setBcc(@NonNull String... bcc) {
return setPropertyString(KEY_BCC, bcc);
@@ -202,6 +207,7 @@
/**
* Sets the subject of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setSubject(@NonNull String subject) {
return setPropertyString(KEY_SUBJECT, subject);
@@ -210,6 +216,7 @@
/**
* Sets the body of {@link AppSearchEmail}
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setBody(@NonNull String body) {
return setPropertyString(KEY_BODY, body);
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 7b7f92e..61fa4ed 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -46,6 +46,7 @@
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.StringProperty {
method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
+ method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
@@ -80,6 +81,7 @@
method public boolean isSuccess();
method public static <ValueType> androidx.appsearch.app.AppSearchResult<ValueType!> newFailedResult(int, String?);
method public static <ValueType> androidx.appsearch.app.AppSearchResult<ValueType!> newSuccessfulResult(ValueType?);
+ field public static final int RESULT_DENIED = 9; // 0x9
field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2
field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3
field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7
@@ -163,6 +165,7 @@
}
public static final class AppSearchSchema.StringPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
+ method public boolean getDeletionPropagation();
method public int getIndexingType();
method public int getJoinableValueType();
method public int getTokenizerType();
@@ -181,6 +184,7 @@
ctor public AppSearchSchema.StringPropertyConfig.Builder(String);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig build();
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
+ method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DELETION_PROPAGATION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDeletionPropagation(boolean);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setJoinableValueType(int);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
@@ -199,11 +203,13 @@
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> reportUsageAsync(androidx.appsearch.app.ReportUsageRequest);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> requestFlushAsync();
method public androidx.appsearch.app.SearchResults search(String, androidx.appsearch.app.SearchSpec);
+ method public com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.appsearch.app.SearchSuggestionResult!>!> searchSuggestionAsync(String, androidx.appsearch.app.SearchSuggestionSpec);
method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.SetSchemaResponse!> setSchemaAsync(androidx.appsearch.app.SetSchemaRequest);
}
public interface DocumentClassFactory<T> {
method public T fromGenericDocument(androidx.appsearch.app.GenericDocument) throws androidx.appsearch.exceptions.AppSearchException;
+ method public java.util.List<java.lang.Class<?>!> getNestedDocumentClasses() throws androidx.appsearch.exceptions.AppSearchException;
method public androidx.appsearch.app.AppSearchSchema getSchema() throws androidx.appsearch.exceptions.AppSearchException;
method public String getSchemaName();
method public androidx.appsearch.app.GenericDocument toGenericDocument(T) throws androidx.appsearch.exceptions.AppSearchException;
@@ -218,9 +224,12 @@
field public static final String JOIN_SPEC_AND_QUALIFIED_ID = "JOIN_SPEC_AND_QUALIFIED_ID";
field public static final String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
field public static final String NUMERIC_SEARCH = "NUMERIC_SEARCH";
+ field public static final String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
field public static final String SEARCH_RESULT_MATCH_INFO_SUBMATCH = "SEARCH_RESULT_MATCH_INFO_SUBMATCH";
field public static final String SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION = "SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION";
+ field public static final String SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA = "SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA";
field public static final String SEARCH_SPEC_PROPERTY_WEIGHTS = "SEARCH_SPEC_PROPERTY_WEIGHTS";
+ field public static final String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
field public static final String TOKENIZER_TYPE_RFC822 = "TOKENIZER_TYPE_RFC822";
field public static final String VERBATIM_SEARCH = "VERBATIM_SEARCH";
}
@@ -329,7 +338,6 @@
field public static final int AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL = 0; // 0x0
field public static final int AGGREGATION_SCORING_RESULT_COUNT = 1; // 0x1
field public static final int AGGREGATION_SCORING_SUM_RANKING_SIGNAL = 5; // 0x5
- field public static final String QUALIFIED_ID = "this.qualifiedId()";
}
public static final class JoinSpec.Builder {
@@ -495,6 +503,7 @@
method public boolean isVerbatimSearchEnabled();
field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2
field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1
+ field @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA) public static final int GROUPING_TYPE_PER_SCHEMA = 4; // 0x4
field public static final int ORDER_ASCENDING = 1; // 0x1
field public static final int ORDER_DESCENDING = 0; // 0x0
field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
@@ -546,7 +555,7 @@
method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.VERBATIM_SEARCH) public androidx.appsearch.app.SearchSpec.Builder setVerbatimSearchEnabled(boolean);
}
- public class SearchSuggestionResult {
+ public final class SearchSuggestionResult {
method public String getSuggestedResult();
}
@@ -556,7 +565,7 @@
method public androidx.appsearch.app.SearchSuggestionResult.Builder setSuggestedResult(String);
}
- public class SearchSuggestionSpec {
+ public final class SearchSuggestionSpec {
method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterDocumentIds();
method public java.util.List<java.lang.String!> getFilterNamespaces();
method public java.util.List<java.lang.String!> getFilterSchemas();
@@ -577,7 +586,7 @@
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.lang.String!...);
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
- method public androidx.appsearch.app.SearchSuggestionSpec build() throws androidx.appsearch.exceptions.AppSearchException;
+ method public androidx.appsearch.app.SearchSuggestionSpec build();
method public androidx.appsearch.app.SearchSuggestionSpec.Builder setRankingStrategy(int);
}
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 7b7f92e..61fa4ed 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -46,6 +46,7 @@
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.StringProperty {
method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
+ method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
@@ -80,6 +81,7 @@
method public boolean isSuccess();
method public static <ValueType> androidx.appsearch.app.AppSearchResult<ValueType!> newFailedResult(int, String?);
method public static <ValueType> androidx.appsearch.app.AppSearchResult<ValueType!> newSuccessfulResult(ValueType?);
+ field public static final int RESULT_DENIED = 9; // 0x9
field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2
field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3
field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7
@@ -163,6 +165,7 @@
}
public static final class AppSearchSchema.StringPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
+ method public boolean getDeletionPropagation();
method public int getIndexingType();
method public int getJoinableValueType();
method public int getTokenizerType();
@@ -181,6 +184,7 @@
ctor public AppSearchSchema.StringPropertyConfig.Builder(String);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig build();
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
+ method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DELETION_PROPAGATION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDeletionPropagation(boolean);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setJoinableValueType(int);
method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
@@ -199,11 +203,13 @@
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> reportUsageAsync(androidx.appsearch.app.ReportUsageRequest);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> requestFlushAsync();
method public androidx.appsearch.app.SearchResults search(String, androidx.appsearch.app.SearchSpec);
+ method public com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.appsearch.app.SearchSuggestionResult!>!> searchSuggestionAsync(String, androidx.appsearch.app.SearchSuggestionSpec);
method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.SetSchemaResponse!> setSchemaAsync(androidx.appsearch.app.SetSchemaRequest);
}
public interface DocumentClassFactory<T> {
method public T fromGenericDocument(androidx.appsearch.app.GenericDocument) throws androidx.appsearch.exceptions.AppSearchException;
+ method public java.util.List<java.lang.Class<?>!> getNestedDocumentClasses() throws androidx.appsearch.exceptions.AppSearchException;
method public androidx.appsearch.app.AppSearchSchema getSchema() throws androidx.appsearch.exceptions.AppSearchException;
method public String getSchemaName();
method public androidx.appsearch.app.GenericDocument toGenericDocument(T) throws androidx.appsearch.exceptions.AppSearchException;
@@ -218,9 +224,12 @@
field public static final String JOIN_SPEC_AND_QUALIFIED_ID = "JOIN_SPEC_AND_QUALIFIED_ID";
field public static final String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
field public static final String NUMERIC_SEARCH = "NUMERIC_SEARCH";
+ field public static final String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
field public static final String SEARCH_RESULT_MATCH_INFO_SUBMATCH = "SEARCH_RESULT_MATCH_INFO_SUBMATCH";
field public static final String SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION = "SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION";
+ field public static final String SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA = "SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA";
field public static final String SEARCH_SPEC_PROPERTY_WEIGHTS = "SEARCH_SPEC_PROPERTY_WEIGHTS";
+ field public static final String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
field public static final String TOKENIZER_TYPE_RFC822 = "TOKENIZER_TYPE_RFC822";
field public static final String VERBATIM_SEARCH = "VERBATIM_SEARCH";
}
@@ -329,7 +338,6 @@
field public static final int AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL = 0; // 0x0
field public static final int AGGREGATION_SCORING_RESULT_COUNT = 1; // 0x1
field public static final int AGGREGATION_SCORING_SUM_RANKING_SIGNAL = 5; // 0x5
- field public static final String QUALIFIED_ID = "this.qualifiedId()";
}
public static final class JoinSpec.Builder {
@@ -495,6 +503,7 @@
method public boolean isVerbatimSearchEnabled();
field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2
field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1
+ field @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA) public static final int GROUPING_TYPE_PER_SCHEMA = 4; // 0x4
field public static final int ORDER_ASCENDING = 1; // 0x1
field public static final int ORDER_DESCENDING = 0; // 0x0
field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
@@ -546,7 +555,7 @@
method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.VERBATIM_SEARCH) public androidx.appsearch.app.SearchSpec.Builder setVerbatimSearchEnabled(boolean);
}
- public class SearchSuggestionResult {
+ public final class SearchSuggestionResult {
method public String getSuggestedResult();
}
@@ -556,7 +565,7 @@
method public androidx.appsearch.app.SearchSuggestionResult.Builder setSuggestedResult(String);
}
- public class SearchSuggestionSpec {
+ public final class SearchSuggestionSpec {
method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterDocumentIds();
method public java.util.List<java.lang.String!> getFilterNamespaces();
method public java.util.List<java.lang.String!> getFilterSchemas();
@@ -577,7 +586,7 @@
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.lang.String!...);
method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
- method public androidx.appsearch.app.SearchSuggestionSpec build() throws androidx.appsearch.exceptions.AppSearchException;
+ method public androidx.appsearch.app.SearchSuggestionSpec build();
method public androidx.appsearch.app.SearchSuggestionSpec.Builder setRankingStrategy(int);
}
diff --git a/appsearch/appsearch/build.gradle b/appsearch/appsearch/build.gradle
index f991e23..c4fbedbb 100644
--- a/appsearch/appsearch/build.gradle
+++ b/appsearch/appsearch/build.gradle
@@ -40,9 +40,15 @@
implementation('androidx.core:core:1.7.0')
androidTestAnnotationProcessor project(':appsearch:appsearch-compiler')
+ androidTestImplementation project(':appsearch:appsearch-builtin-types')
androidTestImplementation project(':appsearch:appsearch-local-storage')
androidTestImplementation project(':appsearch:appsearch-platform-storage')
+ androidTestImplementation project(':appsearch:appsearch-play-services-storage')
androidTestImplementation project(':appsearch:appsearch-test-util')
+ // Needed to check if PlayServicesAppSearch throws ApiException.
+ androidTestImplementation("com.google.android.gms:play-services-basement:18.1.0", {
+ exclude group: "androidx.core", module: "core"
+ })
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
diff --git a/appsearch/appsearch/lint-baseline.xml b/appsearch/appsearch/lint-baseline.xml
index 00718c8..0f84e2d 100644
--- a/appsearch/appsearch/lint-baseline.xml
+++ b/appsearch/appsearch/lint-baseline.xml
@@ -13,15 +13,6 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1="public interface AppSearchObserverCallback extends ObserverCallback {}"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/appsearch/observer/AppSearchObserverCallback.java"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
errorLine1=" public @interface ResultCode {}"
errorLine2=" ~~~~~~~~~~">
<location
@@ -139,8 +130,8 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" public @DataType int getDataType() {"
- errorLine2=" ~~~~~~~~~~~">
+ errorLine1=" public int getDataType() {"
+ errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/androidx/appsearch/app/AppSearchSchema.java"/>
</issue>
@@ -211,6 +202,15 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1="public @interface CanIgnoreReturnValue {}"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/annotation/CanIgnoreReturnValue.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1="public final class DocumentClassFactoryRegistry {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -220,6 +220,24 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1="public interface FeatureConstants {"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/app/FeatureConstants.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/app/Features.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1=" public GenericDocument(@NonNull Bundle bundle) {"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
index 2e657eb..1f25e64 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
@@ -17,15 +17,26 @@
package androidx.appsearch.app;
import static androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES;
+import static androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID;
import static androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
import static androidx.appsearch.testutil.AppSearchTestUtils.convertSearchResultsToDocuments;
+import static androidx.appsearch.testutil.AppSearchTestUtils.doGet;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
import androidx.annotation.NonNull;
import androidx.appsearch.annotation.Document;
+import androidx.appsearch.builtintypes.PotentialAction;
+import androidx.appsearch.builtintypes.Thing;
+import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.testutil.AppSearchEmail;
+import androidx.appsearch.util.DocumentIdUtil;
+import androidx.test.core.app.ApplicationProvider;
import com.google.auto.value.AutoValue;
import com.google.common.util.concurrent.ListenableFuture;
@@ -40,6 +51,8 @@
public abstract class AnnotationProcessorTestBase {
private AppSearchSession mSession;
+ private static final String TEST_PACKAGE_NAME =
+ ApplicationProvider.getApplicationContext().getPackageName();
private static final String DB_NAME_1 = "";
protected abstract ListenableFuture<AppSearchSession> createSearchSessionAsync(
@@ -241,7 +254,7 @@
assertThat(first.toArray()).isEqualTo(second.toArray());
}
- public static Gift createPopulatedGift() {
+ public static Gift createPopulatedGift() throws AppSearchException {
Gift gift = new Gift();
gift.mNamespace = "gift.namespace";
gift.mId = "gift.id";
@@ -295,6 +308,33 @@
}
}
+
+ @Document
+ static class CardAction {
+ @Document.Namespace
+ String mNamespace;
+
+ @Document.Id
+ String mId;
+ @Document.StringProperty(name = "cardRef",
+ joinableValueType = JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ String mCardReference; // 3a
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof CardAction)) {
+ return false;
+ }
+ CardAction otherGift = (CardAction) other;
+ assertThat(otherGift.mNamespace).isEqualTo(this.mNamespace);
+ assertThat(otherGift.mId).isEqualTo(this.mId);
+ assertThat(otherGift.mCardReference).isEqualTo(this.mCardReference);
+ return true;
+ }
+ }
+
@Test
public void testAnnotationProcessor() throws Exception {
//TODO(b/156296904) add test for int, float, GenericDocument, and class with
@@ -323,9 +363,9 @@
@Test
public void testAnnotationProcessor_queryByType() throws Exception {
mSession.setSchemaAsync(
- new SetSchemaRequest.Builder()
- .addDocumentClasses(Card.class, Gift.class)
- .addSchemas(AppSearchEmail.SCHEMA).build())
+ new SetSchemaRequest.Builder()
+ .addDocumentClasses(Card.class, Gift.class)
+ .addSchemas(AppSearchEmail.SCHEMA).build())
.get();
// Create documents and index them
@@ -377,6 +417,61 @@
}
@Test
+ public void testAnnotationProcessor_simpleJoin() throws Exception {
+ assumeTrue(mSession.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+ mSession.setSchemaAsync(
+ new SetSchemaRequest.Builder()
+ .addDocumentClasses(Card.class, CardAction.class)
+ .build())
+ .get();
+
+ // Index a Card and a Gift referencing it.
+ Card peetsCard = new Card();
+ peetsCard.mNamespace = "personal";
+ peetsCard.mId = "peets1";
+ CardAction bdayGift = new CardAction();
+ bdayGift.mNamespace = "personal";
+ bdayGift.mId = "2023-jan-31";
+ bdayGift.mCardReference = DocumentIdUtil.createQualifiedId(TEST_PACKAGE_NAME, DB_NAME_1,
+ GenericDocument.fromDocumentClass(peetsCard));
+ checkIsBatchResultSuccess(mSession.putAsync(
+ new PutDocumentsRequest.Builder().addDocuments(peetsCard, bdayGift).build()));
+
+ // Retrieve cards with any given gifts.
+ SearchSpec innerSpec = new SearchSpec.Builder()
+ .addFilterDocumentClasses(CardAction.class)
+ .build();
+ JoinSpec js = new JoinSpec.Builder("cardRef")
+ .setNestedSearch(/*nestedQuery*/ "", innerSpec)
+ .build();
+ SearchResults resultsIter = mSession.search(/*queryExpression*/ "",
+ new SearchSpec.Builder()
+ .addFilterDocumentClasses(Card.class)
+ .setJoinSpec(js)
+ .build());
+
+ // Verify that search results include card(s) joined with gift(s).
+ List<SearchResult> results = resultsIter.getNextPageAsync().get();
+ assertThat(results).hasSize(1);
+ GenericDocument cardResultDoc = results.get(0).getGenericDocument();
+ assertThat(cardResultDoc.getId()).isEqualTo(peetsCard.mId);
+ List<SearchResult> joinedCardResults = results.get(0).getJoinedResults();
+ assertThat(joinedCardResults).hasSize(1);
+ GenericDocument giftResultDoc = joinedCardResults.get(0).getGenericDocument();
+ assertThat(giftResultDoc.getId()).isEqualTo(bdayGift.mId);
+ }
+
+ @Test
+ public void testAnnotationProcessor_onTAndBelow_joinNotSupported() throws Exception {
+ assumeFalse(mSession.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+ Exception e = assertThrows(UnsupportedOperationException.class,
+ () -> mSession.setSchemaAsync(
+ new SetSchemaRequest.Builder()
+ .addDocumentClasses(Card.class, CardAction.class)
+ .build()));
+ }
+
+ @Test
public void testGenericDocumentConversion() throws Exception {
Gift inGift = Gift.createPopulatedGift();
GenericDocument genericDocument1 = GenericDocument.fromDocumentClass(inGift);
@@ -472,4 +567,123 @@
List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
assertThat(documents).containsExactly(genericDocument);
}
+
+ @Test
+ public void testActionDocumentPutAndRetrieveHelper() throws Exception {
+ String namespace = "namespace";
+ String id = "docId";
+ String name = "View";
+ String uri = "package://view";
+ String description = "View action";
+ long creationMillis = 300;
+
+ GenericDocument genericDocAction = new GenericDocument.Builder<>(namespace, id,
+ "builtin:PotentialAction")
+ .setPropertyString("name", name)
+ .setPropertyString("uri", uri)
+ .setPropertyString("description", description)
+ .setCreationTimestampMillis(creationMillis)
+ .build();
+
+ mSession.setSchemaAsync(
+ new SetSchemaRequest.Builder().addDocumentClasses(PotentialAction.class)
+ .setForceOverride(true).build()).get();
+ checkIsBatchResultSuccess(
+ mSession.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(
+ genericDocAction).build()));
+
+ GetByDocumentIdRequest request = new GetByDocumentIdRequest.Builder(namespace)
+ .addIds(id)
+ .build();
+ List<GenericDocument> outDocuments = doGet(mSession, request);
+ assertThat(outDocuments).hasSize(1);
+ PotentialAction potentialAction =
+ outDocuments.get(0).toDocumentClass(PotentialAction.class);
+
+ assertThat(potentialAction.getName()).isEqualTo(name);
+ assertThat(potentialAction.getUri()).isEqualTo(uri);
+ assertThat(potentialAction.getDescription()).isEqualTo(description);
+ }
+
+ @Test
+ public void testDependentSchemas() throws Exception {
+ // Test that makes sure if you call setSchema on Thing, PotentialAction also goes in.
+ String namespace = "namespace";
+ String name = "View";
+ String uri = "package://view";
+ String description = "View action";
+ long creationMillis = 300;
+
+ GenericDocument genericDocAction = new GenericDocument.Builder<>(namespace, "actionid",
+ "builtin:PotentialAction")
+ .setPropertyString("name", name)
+ .setPropertyString("uri", uri)
+ .setPropertyString("description", description)
+ .setCreationTimestampMillis(creationMillis)
+ .build();
+
+ Thing thing = new Thing.Builder(namespace, "thingid")
+ .setName(name)
+ .setCreationTimestampMillis(creationMillis).build();
+
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addDocumentClasses(Thing.class)
+ .setForceOverride(true).build();
+
+ // Both Thing and PotentialAction should be set as schemas
+ assertThat(request.getSchemas()).hasSize(2);
+ mSession.setSchemaAsync(request).get();
+
+ assertThat(mSession.getSchemaAsync().get().getSchemas()).hasSize(2);
+
+ // We should be able to put a PotentialAction as well as a Thing
+ checkIsBatchResultSuccess(
+ mSession.putAsync(new PutDocumentsRequest.Builder()
+ .addDocuments(thing)
+ .addGenericDocuments(genericDocAction)
+ .build()));
+
+ GetByDocumentIdRequest getDocRequest = new GetByDocumentIdRequest.Builder(namespace)
+ .addIds("thingid")
+ .build();
+ List<GenericDocument> outDocuments = doGet(mSession, getDocRequest);
+ assertThat(outDocuments).hasSize(1);
+ Thing potentialAction = outDocuments.get(0).toDocumentClass(Thing.class);
+
+ assertThat(potentialAction.getNamespace()).isEqualTo(namespace);
+ assertThat(potentialAction.getId()).isEqualTo("thingid");
+ assertThat(potentialAction.getName()).isEqualTo(name);
+ assertThat(potentialAction.getPotentialActions()).isEmpty();
+ }
+
+ @Document
+ static class Outer {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Middle mMiddle;
+ }
+
+ @Document
+ static class Middle {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Inner mInner;
+ }
+
+ @Document
+ static class Inner {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.StringProperty String mContents;
+ }
+
+ @Test
+ public void testMultipleDependentSchemas() throws Exception {
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addDocumentClasses(Outer.class)
+ .setForceOverride(true).build();
+
+ // Outer, as well as Middle and Inner should be set.
+ assertThat(request.getSchemas()).hasSize(3);
+ mSession.setSchemaAsync(request).get();
+ assertThat(mSession.getSchemaAsync().get().getSchemas()).hasSize(3);
+ }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
index 8310664..c289e9e 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
@@ -17,12 +17,18 @@
package androidx.appsearch.app;
import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
+import static androidx.appsearch.testutil.AppSearchTestUtils.convertSearchResultsToDocuments;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
import androidx.annotation.NonNull;
import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.testutil.AppSearchEmail;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
@@ -32,6 +38,7 @@
import org.junit.Test;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
public abstract class AppSearchSessionInternalTestBase {
@@ -167,4 +174,382 @@
.build()).get();
assertThat(suggestions).containsExactly(resultOne, resultThree, resultFour);
}
+
+ // TODO(b/258715421): move this test to cts test once we un-hide schema type grouping API.
+ @Test
+ public void testQuery_ResultGroupingLimits_SchemaGroupingSupported() throws Exception {
+ assumeTrue(mDb1.getFeatures()
+ .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+ // Schema registration
+ AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
+ .addProperty(new StringPropertyConfig.Builder("foo")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build()
+ ).build();
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .addSchemas(genericSchema)
+ .build())
+ .get();
+
+ // Index four documents.
+ AppSearchEmail inEmail1 =
+ new AppSearchEmail.Builder("namespace1", "id1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+ AppSearchEmail inEmail2 =
+ new AppSearchEmail.Builder("namespace1", "id2")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+ AppSearchEmail inEmail3 =
+ new AppSearchEmail.Builder("namespace2", "id3")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+ AppSearchEmail inEmail4 =
+ new AppSearchEmail.Builder("namespace2", "id4")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+ AppSearchEmail inEmail5 =
+ new AppSearchEmail.Builder("namespace2", "id5")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail5).build()));
+ GenericDocument inDoc1 =
+ new GenericDocument.Builder<>("namespace3", "id6", "Generic")
+ .setPropertyString("foo", "body").build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inDoc1).build()));
+ GenericDocument inDoc2 =
+ new GenericDocument.Builder<>("namespace3", "id7", "Generic")
+ .setPropertyString("foo", "body").build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inDoc2).build()));
+ GenericDocument inDoc3 =
+ new GenericDocument.Builder<>("namespace4", "id8", "Generic")
+ .setPropertyString("foo", "body").build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inDoc3).build()));
+
+ // Query with per package result grouping. Only the last document 'doc3' should be
+ // returned.
+ SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3);
+
+ // Query with per namespace result grouping. Only the last document in each namespace should
+ // be returned ('doc3', 'doc2', 'email5' and 'email2').
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+ // Query with per namespace result grouping. Two of the last documents in each namespace
+ // should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4', 'email2', 'email1')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
+ inEmail1);
+
+ // Query with per schema result grouping. Only the last document of each schema type should
+ // be returned ('doc3', 'email5')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+ // Query with per schema result grouping. Only the last two documents of each schema type
+ // should be returned ('doc3', 'doc2', 'email5', 'email4')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+ // Query with per package and per namespace result grouping. Only the last document in each
+ // namespace should be returned ('doc3', 'doc2', 'email5' and 'email2').
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+ // Query with per package and per namespace result grouping. Only the last two documents
+ // in each namespace should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+ // 'email2', 'email1')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
+ inEmail1);
+
+ // Query with per package and per schema type result grouping. Only the last document in
+ // each schema type should be returned. ('doc3', 'email5')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_SCHEMA
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+ // Query with per package and per schema type result grouping. Only the last two document in
+ // each schema type should be returned. ('doc3', 'doc2', 'email5', 'email4')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_SCHEMA
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+ // Query with per namespace and per schema type result grouping. Only the last document in
+ // each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2').
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+ // Query with per namespace and per schema type result grouping. Only the last two documents
+ // in each namespace should be returned. ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+ // 'email2', 'email1')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
+ inEmail1);
+
+ // Query with per namespace, per package and per schema type result grouping. Only the last
+ // document in each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+ // Query with per namespace, per package and per schema type result grouping. Only the last
+ // two documents in each namespace should be returned.('doc3', 'doc2', 'doc1', 'email5',
+ // 'email4', 'email2', 'email1')
+ searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
+ inEmail1);
+ }
+
+ // TODO(b/258715421): move this test to cts test once we un-hide schema type grouping API.
+ @Test
+ public void testQuery_ResultGroupingLimits_SchemaGroupingNotSupported() throws Exception {
+ assumeFalse(mDb1.getFeatures()
+ .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+ // Schema registration
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA).build()).get();
+
+ // Index four documents.
+ AppSearchEmail inEmail1 =
+ new AppSearchEmail.Builder("namespace1", "id1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+ AppSearchEmail inEmail2 =
+ new AppSearchEmail.Builder("namespace1", "id2")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+ AppSearchEmail inEmail3 =
+ new AppSearchEmail.Builder("namespace2", "id3")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+ AppSearchEmail inEmail4 =
+ new AppSearchEmail.Builder("namespace2", "id4")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+
+ // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+ // UnsupportedOperationException will be thrown.
+ SearchSpec searchSpec1 = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
+ .build();
+ UnsupportedOperationException exception = assertThrows(UnsupportedOperationException.class,
+ () -> mDb1.search("body", searchSpec1));
+ assertThat(exception).hasMessageThat().contains(
+ Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
+ + " AppSearch implementation.");
+
+ // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+ // UnsupportedOperationException will be thrown.
+ SearchSpec searchSpec2 = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
+ .build();
+ exception = assertThrows(UnsupportedOperationException.class,
+ () -> mDb1.search("body", searchSpec2));
+ assertThat(exception).hasMessageThat().contains(
+ Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
+ + " AppSearch implementation.");
+
+ // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+ // UnsupportedOperationException will be thrown.
+ SearchSpec searchSpec3 = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
+ .build();
+ exception = assertThrows(UnsupportedOperationException.class,
+ () -> mDb1.search("body", searchSpec3));
+ assertThat(exception).hasMessageThat().contains(
+ Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
+ + " AppSearch implementation.");
+
+ // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+ // UnsupportedOperationException will be thrown.
+ SearchSpec searchSpec4 = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .build();
+ exception = assertThrows(UnsupportedOperationException.class,
+ () -> mDb1.search("body", searchSpec4));
+ assertThat(exception).hasMessageThat().contains(
+ Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
+ + " AppSearch implementation.");
+ }
+
+ // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
+ @Test
+ public void testGetSchema_joinableValueType() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(
+ Features.SCHEMA_SET_DELETION_PROPAGATION));
+ AppSearchSchema inSchema = new AppSearchSchema.Builder("Test")
+ .addProperty(new StringPropertyConfig.Builder("normalStr")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build()
+ ).addProperty(new StringPropertyConfig.Builder("optionalQualifiedIdStr")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .build()
+ ).addProperty(new StringPropertyConfig.Builder("requiredQualifiedIdStr")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setDeletionPropagation(true)
+ .build()
+ ).build();
+
+ SetSchemaRequest request = new SetSchemaRequest.Builder()
+ .addSchemas(inSchema).build();
+
+ mDb1.setSchemaAsync(request).get();
+
+ Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
+ assertThat(actual).hasSize(1);
+ assertThat(actual).containsExactlyElementsIn(request.getSchemas());
+ }
+
+ // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
+ @Test
+ public void testGetSchema_deletionPropagation_unsupported() {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(
+ Features.SCHEMA_SET_DELETION_PROPAGATION));
+ AppSearchSchema schema = new AppSearchSchema.Builder("Test")
+ .addProperty(new StringPropertyConfig.Builder("qualifiedIdDeletionPropagation")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setDeletionPropagation(true)
+ .build()
+ ).build();
+ SetSchemaRequest request = new SetSchemaRequest.Builder()
+ .addSchemas(schema).build();
+ Exception e = assertThrows(UnsupportedOperationException.class, () ->
+ mDb1.setSchemaAsync(request).get());
+ assertThat(e.getMessage()).isEqualTo("Setting deletion propagation is not supported "
+ + "on this AppSearch implementation.");
+ }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java
index 3130c92..bc68f37 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java
@@ -16,14 +16,10 @@
package androidx.appsearch.app;
-import static androidx.appsearch.app.AppSearchResult.RESULT_INVALID_ARGUMENT;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import androidx.appsearch.exceptions.AppSearchException;
-
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -54,12 +50,11 @@
@Test
public void testPropertyFilterMustMatchSchemaFilter() throws Exception {
- AppSearchException e = assertThrows(AppSearchException.class,
+ IllegalStateException e = assertThrows(IllegalStateException.class,
() -> new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
.addFilterSchemas("Person")
.addFilterProperties("Email", ImmutableList.of("Subject", "body"))
.build());
- assertThat(e.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
assertThat(e).hasMessageThat().contains("The schema: Email exists in the "
+ "property filter but doesn't exist in the schema filter.");
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
index 799584a..37e1255 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
@@ -18,8 +18,15 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+
import org.junit.Test;
+import java.util.List;
+
/** Tests for private APIs of {@link SetSchemaResponse}. */
public class SetSchemaResponseInternalTest {
@Test
@@ -67,4 +74,41 @@
assertThat(rebuild.getMigratedTypes()).containsExactly("migrated1", "migrated2");
assertThat(rebuild.getMigrationFailures()).containsExactly(failure1, failure2);
}
+
+ // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
+ @Test
+ public void testPropertyConfig_deletionPropagation() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Test")
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setDeletionPropagation(true)
+ .build())
+ .build();
+
+ assertThat(schema.getSchemaType()).isEqualTo("Test");
+ List<PropertyConfig> properties = schema.getProperties();
+ assertThat(properties).hasSize(1);
+
+ assertThat(properties.get(0).getName()).isEqualTo("qualifiedId1");
+ assertThat(properties.get(0).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
+ assertThat(((StringPropertyConfig) properties.get(0)).getJoinableValueType())
+ .isEqualTo(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+ assertThat(((StringPropertyConfig) properties.get(0)).getDeletionPropagation())
+ .isEqualTo(true);
+ }
+
+ // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
+ @Test
+ public void testStringPropertyConfig_setJoinableProperty_deletePropagationError() {
+ final StringPropertyConfig.Builder builder =
+ new StringPropertyConfig.Builder("qualifiedId")
+ .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+ .setDeletionPropagation(true);
+ IllegalStateException e =
+ assertThrows(IllegalStateException.class, () -> builder.build());
+ assertThat(e).hasMessageThat().contains(
+ "Cannot set deletion propagation without setting a joinable value type");
+ }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationLocalCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationLocalCtsTest.java
index 75b3888..0d0ac6e 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationLocalCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationLocalCtsTest.java
@@ -22,12 +22,10 @@
import androidx.appsearch.app.AppSearchSession;
import androidx.appsearch.localstorage.LocalStorage;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
import com.google.common.util.concurrent.ListenableFuture;
-@FlakyTest(bugId = 242761389)
-public class AppSearchSchemaMigrationLocalCtsTest extends AppSearchSchemaMigrationCtsTestBase{
+public class AppSearchSchemaMigrationLocalCtsTest extends AppSearchSchemaMigrationCtsTestBase {
@Override
protected ListenableFuture<AppSearchSession> createSearchSessionAsync(@NonNull String dbName) {
Context context = ApplicationProvider.getApplicationContext();
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
index bf470fd..6c6fe28 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
@@ -94,10 +94,10 @@
private AppSearchSession mDb2;
protected abstract ListenableFuture<AppSearchSession> createSearchSessionAsync(
- @NonNull String dbName);
+ @NonNull String dbName) throws Exception;
protected abstract ListenableFuture<AppSearchSession> createSearchSessionAsync(
- @NonNull String dbName, @NonNull ExecutorService executor);
+ @NonNull String dbName, @NonNull ExecutorService executor) throws Exception;
@Before
public void setUp() throws Exception {
@@ -147,20 +147,22 @@
AppSearchSchema emailSchema1 = new AppSearchSchema.Builder(AppSearchEmail.SCHEMA_TYPE)
.build();
- Throwable throwable = assertThrows(ExecutionException.class,
- () -> mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
- .addSchemas(emailSchema1).build()).get()).getCause();
- assertThat(throwable).isInstanceOf(AppSearchException.class);
- AppSearchException exception = (AppSearchException) throwable;
+ SetSchemaRequest setSchemaRequest1 =
+ new SetSchemaRequest.Builder().addSchemas(emailSchema1).build();
+ ExecutionException executionException =
+ assertThrows(ExecutionException.class,
+ () -> mDb1.setSchemaAsync(setSchemaRequest1).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_SCHEMA);
assertThat(exception).hasMessageThat().contains("Schema is incompatible.");
assertThat(exception).hasMessageThat().contains("Incompatible types: {builtin:Email}");
- throwable = assertThrows(ExecutionException.class,
- () -> mDb1.setSchemaAsync(new SetSchemaRequest.Builder().build()).get()).getCause();
-
- assertThat(throwable).isInstanceOf(AppSearchException.class);
- exception = (AppSearchException) throwable;
+ SetSchemaRequest setSchemaRequest2 = new SetSchemaRequest.Builder().build();
+ executionException = assertThrows(ExecutionException.class,
+ () -> mDb1.setSchemaAsync(setSchemaRequest2).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_SCHEMA);
assertThat(exception).hasMessageThat().contains("Schema is incompatible.");
assertThat(exception).hasMessageThat().contains("Deleted types: {builtin:Email}");
@@ -515,7 +517,6 @@
@Test
public void testGetSchema_longPropertyIndexingTypeNone_succeeds() throws Exception {
- assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.NUMERIC_SEARCH));
AppSearchSchema inSchema = new AppSearchSchema.Builder("Test")
.addProperty(new LongPropertyConfig.Builder("long")
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
@@ -546,8 +547,10 @@
SetSchemaRequest request = new SetSchemaRequest.Builder()
.addSchemas(inSchema).build();
- assertThrows(UnsupportedOperationException.class, () ->
+ UnsupportedOperationException e = assertThrows(UnsupportedOperationException.class, () ->
mDb1.setSchemaAsync(request).get());
+ assertThat(e.getMessage()).isEqualTo("LongProperty.INDEXING_TYPE_RANGE is not "
+ + "supported on this AppSearch implementation.");
}
@Test
@@ -579,7 +582,6 @@
@Test
public void testGetSchema_joinableValueTypeNone_succeeds() throws Exception {
- assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
AppSearchSchema inSchema = new AppSearchSchema.Builder("Test")
.addProperty(new StringPropertyConfig.Builder("optionalString")
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
@@ -618,8 +620,11 @@
SetSchemaRequest request = new SetSchemaRequest.Builder()
.addSchemas(inSchema).build();
- assertThrows(UnsupportedOperationException.class, () ->
+ UnsupportedOperationException e = assertThrows(UnsupportedOperationException.class, () ->
mDb1.setSchemaAsync(request).get());
+ assertThat(e.getMessage()).isEqualTo(
+ "StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID is not supported on this "
+ + "AppSearch implementation.");
}
@Test
@@ -947,10 +952,11 @@
assertThat(outEmail).isEqualTo(email);
// Try to remove the email schema. This should fail as it's an incompatible change.
- Throwable failResult1 = assertThrows(
- ExecutionException.class,
- () -> mDb1.setSchemaAsync(new SetSchemaRequest.Builder().build()).get()).getCause();
- assertThat(failResult1).isInstanceOf(AppSearchException.class);
+ SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder().build();
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> mDb1.setSchemaAsync(setSchemaRequest).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException failResult1 = (AppSearchException) executionException.getCause();
assertThat(failResult1).hasMessageThat().contains("Schema is incompatible");
assertThat(failResult1).hasMessageThat().contains(
"Deleted types: {builtin:Email}");
@@ -1020,10 +1026,11 @@
// Try to remove the email schema in database1. This should fail as it's an incompatible
// change.
- Throwable failResult1 = assertThrows(
- ExecutionException.class,
- () -> mDb1.setSchemaAsync(new SetSchemaRequest.Builder().build()).get()).getCause();
- assertThat(failResult1).isInstanceOf(AppSearchException.class);
+ SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder().build();
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> mDb1.setSchemaAsync(setSchemaRequest).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException failResult1 = (AppSearchException) executionException.getCause();
assertThat(failResult1).hasMessageThat().contains("Schema is incompatible");
assertThat(failResult1).hasMessageThat().contains(
"Deleted types: {builtin:Email}");
@@ -1619,21 +1626,17 @@
checkIsBatchResultSuccess(mDb1.putAsync(
new PutDocumentsRequest.Builder().addGenericDocuments(doc).build()));
- // TODO(b/208654892); Remove setListFilterQueryLanguageEnabled once advanced query is fully
- // supported.
// Query for the document
// Use advanced query but disable NUMERIC_SEARCH in the SearchSpec.
SearchResults searchResults = mDb1.search("price < 20",
new SearchSpec.Builder()
- .setListFilterQueryLanguageEnabled(true)
.setNumericSearchEnabled(false)
.build());
- Throwable failResult = assertThrows(
- ExecutionException.class,
- () -> searchResults.getNextPageAsync().get()).getCause();
- assertThat(failResult).isInstanceOf(AppSearchException.class);
- AppSearchException exception = (AppSearchException) failResult;
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> searchResults.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
assertThat(exception).hasMessageThat().contains(Features.NUMERIC_SEARCH);
@@ -1738,6 +1741,130 @@
}
@Test
+ public void testQuery_advancedRankingWithPropertyWeights() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(
+ Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION));
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(
+ Features.SEARCH_SPEC_PROPERTY_WEIGHTS));
+
+ // Schema registration
+ mDb1.setSchemaAsync(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .build()).get();
+
+ // Index a document
+ AppSearchEmail inEmail =
+ new AppSearchEmail.Builder("namespace", "id1")
+ .setFrom("test from")
+ .setTo("test to")
+ .setSubject("subject")
+ .setBody("test body")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build()));
+
+ // Query for the document, and set an advanced ranking expression that evaluates to 0.7.
+ SearchResults searchResults = mDb1.search("test", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setPropertyWeights(AppSearchEmail.SCHEMA_TYPE,
+ ImmutableMap.of("from", 0.1, "to", 0.2,
+ "subject", 2.0, "body", 0.4))
+ // this.propertyWeights() returns normalized property weights, in which each
+ // weight is divided by the maximum weight.
+ // As a result, this expression will evaluates to the list {0.1 / 2.0, 0.2 / 2.0,
+ // 0.4 / 2.0}, since the matched properties are "from", "to" and "body", and the
+ // maximum weight provided is 2.0.
+ // Thus, sum(this.propertyWeights()) will be evaluated to 0.05 + 0.1 + 0.2 = 0.35.
+ .setRankingStrategy("sum(this.propertyWeights())")
+ .build());
+ List<SearchResult> results = retrieveAllSearchResults(searchResults);
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getGenericDocument()).isEqualTo(inEmail);
+ assertThat(results.get(0).getRankingSignal()).isEqualTo(0.35);
+ }
+
+ @Test
+ public void testQuery_advancedRankingWithJoin() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(
+ Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION));
+ assumeTrue(mDb1.getFeatures()
+ .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+ // A full example of how join might be used
+ AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
+ .addProperty(new StringPropertyConfig.Builder("entityId")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build()
+ ).addProperty(new StringPropertyConfig.Builder("note")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build()
+ ).build();
+
+ // Schema registration
+ mDb1.setSchemaAsync(
+ new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA, actionSchema)
+ .build()).get();
+
+ // Index a document
+ AppSearchEmail inEmail =
+ new AppSearchEmail.Builder("namespace", "id1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .setScore(1)
+ .build();
+
+ String qualifiedId = DocumentIdUtil.createQualifiedId(mContext.getPackageName(), DB_NAME_1,
+ "namespace", "id1");
+ GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id2", "ViewAction")
+ .setScore(1)
+ .setPropertyString("entityId", qualifiedId)
+ .setPropertyString("note", "Viewed email on Monday").build();
+ GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
+ .setScore(2)
+ .setPropertyString("entityId", qualifiedId)
+ .setPropertyString("note", "Viewed email on Tuesday").build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, viewAction1,
+ viewAction2).build()));
+
+ SearchSpec nestedSearchSpec =
+ new SearchSpec.Builder()
+ .setRankingStrategy("2 * this.documentScore()")
+ .setOrder(SearchSpec.ORDER_ASCENDING)
+ .build();
+
+ JoinSpec js = new JoinSpec.Builder("entityId")
+ .setNestedSearch("", nestedSearchSpec)
+ .build();
+
+ SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
+ // this.childrenRankingSignals() evaluates to the list {1 * 2, 2 * 2}.
+ // Thus, sum(this.childrenRankingSignals()) evaluates to 6.
+ .setRankingStrategy("sum(this.childrenRankingSignals())")
+ .setJoinSpec(js)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build());
+
+ List<SearchResult> sr = searchResults.getNextPageAsync().get();
+
+ assertThat(sr).hasSize(1);
+ assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id1");
+ assertThat(sr.get(0).getJoinedResults()).hasSize(2);
+ assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction1);
+ assertThat(sr.get(0).getJoinedResults().get(1).getGenericDocument()).isEqualTo(viewAction2);
+ assertThat(sr.get(0).getRankingSignal()).isEqualTo(6.0);
+ }
+
+ @Test
public void testQuery_invalidAdvancedRanking() throws Exception {
assumeTrue(mDb1.getFeatures().isFeatureSupported(
Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION));
@@ -1764,16 +1891,54 @@
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.setRankingStrategy("sqrt()")
.build());
- Throwable throwable = assertThrows(ExecutionException.class,
- () -> searchResults.getNextPageAsync().get()).getCause();
- assertThat(throwable).isInstanceOf(AppSearchException.class);
- AppSearchException exception = (AppSearchException) throwable;
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> searchResults.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
assertThat(exception).hasMessageThat().contains(
"Math functions must have at least one argument.");
}
@Test
+ public void testQuery_invalidAdvancedRankingWithChildrenRankingSignals() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(
+ Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION));
+ assumeTrue(mDb1.getFeatures()
+ .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+ // Schema registration
+ mDb1.setSchemaAsync(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .build()).get();
+
+ // Index a document
+ AppSearchEmail inEmail =
+ new AppSearchEmail.Builder("namespace", "id1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build()));
+
+ SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ // Using this.childrenRankingSignals() without the context of a join is invalid.
+ .setRankingStrategy("sum(this.childrenRankingSignals())")
+ .build());
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> searchResults.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
+ assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+ assertThat(exception).hasMessageThat().contains(
+ "childrenRankingSignals must only be used with join");
+ }
+
+ @Test
public void testQuery_unsupportedAdvancedRanking() throws Exception {
// Assume that advanced ranking has not been supported.
assumeFalse(mDb1.getFeatures().isFeatureSupported(
@@ -2972,8 +3137,7 @@
assertThat(doGet(mDb1, "namespace", "id1", "id2", "id3")).hasSize(3);
// Delete the email type
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterSchemas(AppSearchEmail.SCHEMA_TYPE)
.build())
@@ -3024,8 +3188,7 @@
assertThat(doGet(mDb2, "namespace", "id2")).hasSize(1);
// Delete the email type in instance 1
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterSchemas(AppSearchEmail.SCHEMA_TYPE)
.build())
@@ -3086,8 +3249,7 @@
assertThat(doGet(mDb1, /*namespace=*/"document", "id3")).hasSize(1);
// Delete the email namespace
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterNamespaces("email")
.build())
@@ -3142,8 +3304,7 @@
assertThat(doGet(mDb2, /*namespace=*/"email", "id2")).hasSize(1);
// Delete the email namespace in instance 1
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterNamespaces("email")
.build())
@@ -3198,8 +3359,7 @@
assertThat(doGet(mDb2, "namespace", "id2")).hasSize(1);
// Delete the all document in instance 1
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.build())
.get();
@@ -3273,8 +3433,7 @@
assertThat(documents).hasSize(2);
// Delete the all document in instance 1 with TERM_MATCH_PREFIX
- mDb1.removeAsync("",
- new SearchSpec.Builder()
+ mDb1.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.build())
.get();
@@ -3285,8 +3444,7 @@
assertThat(documents).isEmpty();
// Delete the all document in instance 2 with TERM_MATCH_EXACT_ONLY
- mDb2.removeAsync("",
- new SearchSpec.Builder()
+ mDb2.removeAsync("", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.build())
.get();
@@ -3331,7 +3489,7 @@
// Delete the all documents
mDb1.removeAsync("", new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX).build()).get();
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX).build()).get();
// Make sure it's still gone
getResult = mDb1.getByDocumentIdAsync(
@@ -3346,8 +3504,7 @@
assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mDb2.removeAsync("",
- new SearchSpec.Builder()
+ () -> mDb2.removeAsync("", new SearchSpec.Builder()
.setJoinSpec(new JoinSpec.Builder("entityId").build())
.build()));
assertThat(e.getMessage()).isEqualTo("JoinSpec not allowed in removeByQuery, "
@@ -3480,12 +3637,12 @@
mDb1.reportUsageAsync(new ReportUsageRequest.Builder("namespace", "id1").build()).get();
// Use an incorrect namespace; it fails
- ExecutionException e = assertThrows(
- ExecutionException.class,
- () -> mDb1.reportUsageAsync(
- new ReportUsageRequest.Builder("namespace2", "id1").build()).get());
- assertThat(e).hasCauseThat().isInstanceOf(AppSearchException.class);
- AppSearchException cause = (AppSearchException) e.getCause();
+ ReportUsageRequest reportUsageRequest =
+ new ReportUsageRequest.Builder("namespace2", "id1").build();
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> mDb1.reportUsageAsync(reportUsageRequest).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException cause = (AppSearchException) executionException.getCause();
assertThat(cause.getResultCode()).isEqualTo(RESULT_NOT_FOUND);
}
@@ -3596,8 +3753,7 @@
// be returned ('email4' and 'email2').
searchResults = mDb1.search("body", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setResultGrouping(
- SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
.build());
documents = convertSearchResultsToDocuments(searchResults);
assertThat(documents).containsExactly(inEmail4, inEmail2);
@@ -3606,9 +3762,8 @@
// namespace should be returned ('email4' and 'email2').
searchResults = mDb1.search("body", new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setResultGrouping(
- SearchSpec.GROUPING_TYPE_PER_NAMESPACE
- | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+ .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
.build());
documents = convertSearchResultsToDocuments(searchResults);
assertThat(documents).containsExactly(inEmail4, inEmail2);
@@ -3727,10 +3882,10 @@
final SetSchemaRequest newRequest =
new SetSchemaRequest.Builder().addSchemas(newNestedSchema,
newSchema).build();
- Throwable throwable = assertThrows(ExecutionException.class,
- () -> mDb1.setSchemaAsync(newRequest).get()).getCause();
- assertThat(throwable).isInstanceOf(AppSearchException.class);
- AppSearchException exception = (AppSearchException) throwable;
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> mDb1.setSchemaAsync(newRequest).get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_SCHEMA);
assertThat(exception).hasMessageThat().contains("Schema is incompatible.");
assertThat(exception).hasMessageThat().contains("Incompatible types: {TypeA}");
@@ -3840,6 +3995,25 @@
}
@Test
+ public void testRfc822_unsupportedFeature_throwsException() {
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.TOKENIZER_TYPE_RFC822));
+
+ AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+ .addProperty(new StringPropertyConfig.Builder("address")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_RFC822)
+ .build()
+ ).build();
+
+ Exception e = assertThrows(IllegalArgumentException.class, () ->
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+ .setForceOverride(true).addSchemas(emailSchema).build()).get());
+ assertThat(e.getMessage()).isEqualTo("tokenizerType is out of range of [0, 1] (too high)");
+ }
+
+
+ @Test
public void testQuery_verbatimSearch() throws Exception {
assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.VERBATIM_SEARCH));
AppSearchSchema verbatimSchema = new AppSearchSchema.Builder("VerbatimSchema")
@@ -3886,26 +4060,178 @@
.build();
mDb1.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(email).build()).get();
- // TODO(b/208654892) Disable ListFilterQueryLanguage once EXPERIMENTAL_ICING_ADVANCED_QUERY
- // is fully supported.
// ListFilterQueryLanguage is enabled so that EXPERIMENTAL_ICING_ADVANCED_QUERY gets enabled
// in IcingLib.
// Disable VERBATIM_SEARCH in the SearchSpec.
SearchResults searchResults = mDb1.search("\"Hello, world!\"",
new SearchSpec.Builder()
- .setListFilterQueryLanguageEnabled(true)
.setVerbatimSearchEnabled(false)
.build());
- Throwable throwable = assertThrows(ExecutionException.class,
- () -> searchResults.getNextPageAsync().get()).getCause();
- assertThat(throwable).isInstanceOf(AppSearchException.class);
- AppSearchException exception = (AppSearchException) throwable;
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> searchResults.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
assertThat(exception).hasMessageThat().contains(Features.VERBATIM_SEARCH);
}
@Test
+ public void testQuery_listFilterQueryWithEnablingFeatureSucceeds() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema")
+ .addProperty(new StringPropertyConfig.Builder("prop")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build()
+ ).build();
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+ .setForceOverride(true).addSchemas(schema).build()).get();
+
+ GenericDocument email = new GenericDocument.Builder<>(
+ "namespace1", "id1", "Schema")
+ .setPropertyString("prop", "Hello, world!")
+ .build();
+ mDb1.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(email).build()).get();
+
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setListFilterQueryLanguageEnabled(true)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+ // Support for function calls `search`, `createList` was added in list filters
+ SearchResults searchResults = mDb1.search("search(\"hello\", createList(\"prop\"))",
+ searchSpec);
+ List<SearchResult> page = searchResults.getNextPageAsync().get();
+ assertThat(page).hasSize(1);
+ assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
+
+ // Support for prefix operator * was added in list filters.
+ searchResults = mDb1.search("wor*", searchSpec);
+ page = searchResults.getNextPageAsync().get();
+ assertThat(page).hasSize(1);
+ assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
+
+ // Combining negations with compound statements and property restricts was added in list
+ // filters.
+ searchResults = mDb1.search("NOT (foo OR otherProp:hello)", searchSpec);
+ page = searchResults.getNextPageAsync().get();
+ assertThat(page).hasSize(1);
+ assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
+ }
+
+ @Test
+ public void testQuery_listFilterQueryWithoutEnablingFeatureFails() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema")
+ .addProperty(new StringPropertyConfig.Builder("prop")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build()
+ ).build();
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+ .setForceOverride(true).addSchemas(schema).build()).get();
+
+ GenericDocument email = new GenericDocument.Builder<>(
+ "namespace1", "id1", "Schema")
+ .setPropertyString("prop", "Hello, world!")
+ .build();
+ mDb1.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(email).build()).get();
+
+ // Disable LIST_FILTER_QUERY_LANGUAGE in the SearchSpec.
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setListFilterQueryLanguageEnabled(false)
+ .build();
+ SearchResults searchResults = mDb1.search("search(\"hello\", createList(\"prop\"))",
+ searchSpec);
+ ExecutionException executionException = assertThrows(ExecutionException.class,
+ () -> searchResults.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) executionException.getCause();
+ assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+ assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+ assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_QUERY_LANGUAGE);
+
+ SearchResults searchResults2 = mDb1.search("wor*", searchSpec);
+ executionException = assertThrows(ExecutionException.class,
+ () -> searchResults2.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ exception = (AppSearchException) executionException.getCause();
+ assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+ assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+ assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_QUERY_LANGUAGE);
+
+ SearchResults searchResults3 = mDb1.search("NOT (foo OR otherProp:hello)", searchSpec);
+ executionException = assertThrows(ExecutionException.class,
+ () -> searchResults3.getNextPageAsync().get());
+ assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+ exception = (AppSearchException) executionException.getCause();
+ assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+ assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+ assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_QUERY_LANGUAGE);
+ }
+
+ @Test
+ public void testQuery_listFilterQueryFeatures_notSupported() throws Exception {
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.NUMERIC_SEARCH));
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.VERBATIM_SEARCH));
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+
+ // UnsupportedOperationException will be thrown with these queries so no need to
+ // define a schema and index document.
+ SearchSpec.Builder builder = new SearchSpec.Builder();
+ SearchSpec searchSpec1 = builder.setNumericSearchEnabled(true).build();
+ SearchSpec searchSpec2 = builder.setVerbatimSearchEnabled(true).build();
+ SearchSpec searchSpec3 = builder.setListFilterQueryLanguageEnabled(true).build();
+
+ assertThrows(UnsupportedOperationException.class, () ->
+ mDb1.search("\"Hello, world!\"", searchSpec1));
+ assertThrows(UnsupportedOperationException.class, () ->
+ mDb1.search("\"Hello, world!\"", searchSpec2));
+ assertThrows(UnsupportedOperationException.class, () ->
+ mDb1.search("\"Hello, world!\"", searchSpec3));
+ }
+
+ @Test
+ public void testQuery_propertyWeightsNotSupported() throws Exception {
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SPEC_PROPERTY_WEIGHTS));
+
+ // Schema registration
+ mDb1.setSchemaAsync(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .build()).get();
+
+ // Index two documents
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("namespace", "id1")
+ .setCreationTimestampMillis(1000)
+ .setSubject("foo")
+ .build();
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("namespace", "id2")
+ .setCreationTimestampMillis(1000)
+ .setBody("foo")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocuments(email1, email2).build()));
+
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
+ .setOrder(SearchSpec.ORDER_DESCENDING)
+ .setPropertyWeights(AppSearchEmail.SCHEMA_TYPE, ImmutableMap.of("subject",
+ 2.0, "body", 0.5))
+ .build();
+ UnsupportedOperationException exception =
+ assertThrows(UnsupportedOperationException.class,
+ () -> mDb1.search("Hello", searchSpec));
+ assertThat(exception).hasMessageThat().contains("Property weights are not supported");
+ }
+
+ @Test
public void testQuery_propertyWeights() throws Exception {
assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SPEC_PROPERTY_WEIGHTS));
@@ -4159,7 +4485,7 @@
.isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
// A full example of how join might be used
- AppSearchSchema actionSchema = new AppSearchSchema.Builder("BookmarkAction")
+ AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
.addProperty(new StringPropertyConfig.Builder("entityId")
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
@@ -4203,18 +4529,29 @@
String qualifiedId = DocumentIdUtil.createQualifiedId(mContext.getPackageName(), DB_NAME_1,
"namespace", "id1");
- GenericDocument join = new GenericDocument.Builder<>("NS", "id3", "BookmarkAction")
+ GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
+ .setScore(1)
.setPropertyString("entityId", qualifiedId)
- .setPropertyString("note", "Hi this is a joined doc").build();
+ .setPropertyString("note", "Viewed email on Monday").build();
+ GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id4", "ViewAction")
+ .setScore(2)
+ .setPropertyString("entityId", qualifiedId)
+ .setPropertyString("note", "Viewed email on Tuesday").build();
checkIsBatchResultSuccess(mDb1.putAsync(
- new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2, join)
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2,
+ viewAction1, viewAction2)
.build()));
- SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
+ SearchSpec nestedSearchSpec =
+ new SearchSpec.Builder()
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+ .setOrder(SearchSpec.ORDER_ASCENDING)
+ .build();
JoinSpec js = new JoinSpec.Builder("entityId")
.setNestedSearch("", nestedSearchSpec)
.setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+ .setMaxJoinedResultCount(1)
.build();
SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
@@ -4230,8 +4567,11 @@
assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id1");
assertThat(sr.get(0).getJoinedResults()).hasSize(1);
- assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(join);
- assertThat(sr.get(0).getRankingSignal()).isEqualTo(1.0);
+ assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction1);
+ // SearchSpec.Builder#setMaxJoinedResultCount only limits the number of child documents
+ // returned. It does not affect the number of child documents that are scored. So the score
+ // (the COUNT of the number of children) is 2, even though only one child is returned.
+ assertThat(sr.get(0).getRankingSignal()).isEqualTo(2.0);
assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("id2");
assertThat(sr.get(1).getRankingSignal()).isEqualTo(0.0);
@@ -4239,26 +4579,35 @@
}
@Test
- public void testJoinWithoutSupport() throws Exception {
+ public void testJoin_unsupportedFeature_throwsException() throws Exception {
assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
JoinSpec js = new JoinSpec.Builder("entityId").setNestedSearch("", nestedSearchSpec)
.build();
- SearchResults searchResults = mDb1.search("", new SearchSpec.Builder()
- .setJoinSpec(js)
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .build());
+ Exception e = assertThrows(UnsupportedOperationException.class, () -> mDb1.search(
+ /*queryExpression */ "",
+ new SearchSpec.Builder()
+ .setJoinSpec(js)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build()));
+ assertThat(e.getMessage()).isEqualTo("JoinSpec is not available on this AppSearch "
+ + "implementation.");
+ }
- Exception e = assertThrows(UnsupportedOperationException.class, () ->
- searchResults.getNextPageAsync().get());
- assertThat(e).isInstanceOf(UnsupportedOperationException.class);
- assertThat(e.getMessage()).isEqualTo("Searching with a SearchSpec containing a JoinSpec "
- + "is not supported on this AppSearch implementation.");
+ @Test
+ public void testSearchSuggestion_notSupported() throws Exception {
+ assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
+
+ assertThrows(UnsupportedOperationException.class, () ->
+ mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"t",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/2).build()).get());
}
@Test
public void testSearchSuggestion() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4311,6 +4660,7 @@
@Test
public void testSearchSuggestion_namespaceFilter() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4374,6 +4724,7 @@
@Test
public void testSearchSuggestion_documentIdFilter() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4449,6 +4800,7 @@
@Test
public void testSearchSuggestion_schemaFilter() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schemaType1 = new AppSearchSchema.Builder("Type1").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4527,6 +4879,7 @@
@Test
public void testSearchSuggestion_differentPrefix() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4579,6 +4932,7 @@
@Test
public void testSearchSuggestion_differentRankingStrategy() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4645,6 +4999,7 @@
@Test
public void testSearchSuggestion_removeDocument() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4697,6 +5052,7 @@
@Test
public void testSearchSuggestion_replacementDocument() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4747,37 +5103,8 @@
}
@Test
- public void testSearchSuggestion_ignoreOperators() throws Exception {
- // Schema registration
- AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
- new StringPropertyConfig.Builder("body")
- .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
- .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
- .build())
- .build();
- mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
-
- // Index documents
- GenericDocument doc = new GenericDocument.Builder<>("namespace", "id", "Type")
- .setPropertyString("body", "two original")
- .build();
-
- checkIsBatchResultSuccess(mDb1.putAsync(
- new PutDocumentsRequest.Builder().addGenericDocuments(doc)
- .build()));
-
- SearchSuggestionResult resultTwoOriginal =
- new SearchSuggestionResult.Builder().setSuggestedResult("two original").build();
-
- List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
- /*suggestionQueryExpression=*/"two OR",
- new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
- assertThat(suggestions).containsExactly(resultTwoOriginal);
- }
-
- @Test
public void testSearchSuggestion_twoInstances() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
// Schema registration
AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
new StringPropertyConfig.Builder("body")
@@ -4817,4 +5144,120 @@
new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
assertThat(suggestions).isEmpty();
}
+
+ @Test
+ public void testSearchSuggestion_multipleTerms() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
+ // Schema registration
+ AppSearchSchema schema = new AppSearchSchema.Builder("Type").addProperty(
+ new StringPropertyConfig.Builder("body")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build())
+ .build();
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+ // Index documents
+ GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type")
+ .setPropertyString("body", "bar fo")
+ .build();
+ GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type")
+ .setPropertyString("body", "cat foo")
+ .build();
+ GenericDocument doc3 = new GenericDocument.Builder<>("namespace", "id3", "Type")
+ .setPropertyString("body", "fool")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2, doc3)
+ .build()));
+
+ // Search "bar AND f" only document 1 should match the search.
+ List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"bar f",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+ SearchSuggestionResult barFo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("bar fo").build();
+ assertThat(suggestions).containsExactly(barFo);
+
+ // Search for "(bar OR cat) AND f" both document1 "bar fo" and document2 "cat foo" could
+ // match.
+ suggestions = mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"bar OR cat f",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+ SearchSuggestionResult barCatFo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("bar OR cat fo").build();
+ SearchSuggestionResult barCatFoo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("bar OR cat foo").build();
+ assertThat(suggestions).containsExactly(barCatFo, barCatFoo);
+
+ // Search for "(bar AND cat) OR f", all documents could match.
+ suggestions = mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"(bar cat) OR f",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+ SearchSuggestionResult barCatOrFo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("(bar cat) OR fo").build();
+ SearchSuggestionResult barCatOrFoo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("(bar cat) OR foo").build();
+ SearchSuggestionResult barCatOrFool =
+ new SearchSuggestionResult.Builder()
+ .setSuggestedResult("(bar cat) OR fool").build();
+ assertThat(suggestions).containsExactly(barCatOrFo, barCatOrFoo, barCatOrFool);
+
+ // Search for "-bar f", document2 "cat foo" could and document3 "fool" could match.
+ suggestions = mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"-bar f",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+ SearchSuggestionResult noBarFoo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("-bar foo").build();
+ SearchSuggestionResult noBarFool =
+ new SearchSuggestionResult.Builder().setSuggestedResult("-bar fool").build();
+ assertThat(suggestions).containsExactly(noBarFoo, noBarFool);
+ }
+
+ @Test
+ public void testSearchSuggestion_PropertyRestriction() throws Exception {
+ assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
+ // Schema registration
+ AppSearchSchema schema = new AppSearchSchema.Builder("Type")
+ .addProperty(new StringPropertyConfig.Builder("subject")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build())
+ .addProperty(new StringPropertyConfig.Builder("body")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build())
+ .build();
+ mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+ // Index documents
+ GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type")
+ .setPropertyString("subject", "bar fo")
+ .setPropertyString("body", "fool")
+ .build();
+ GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type")
+ .setPropertyString("subject", "bar cat foo")
+ .setPropertyString("body", "fool")
+ .build();
+ GenericDocument doc3 = new GenericDocument.Builder<>("namespace", "ide", "Type")
+ .setPropertyString("subject", "fool")
+ .setPropertyString("body", "fool")
+ .build();
+ checkIsBatchResultSuccess(mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2, doc3)
+ .build()));
+
+ // Search for "bar AND subject:f"
+ List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
+ /*suggestionQueryExpression=*/"bar subject:f",
+ new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+ SearchSuggestionResult barSubjectFo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("bar subject:fo").build();
+ SearchSuggestionResult barSubjectFoo =
+ new SearchSuggestionResult.Builder().setSuggestedResult("bar subject:foo").build();
+ assertThat(suggestions).containsExactly(barSubjectFo, barSubjectFoo);
+ }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java
new file mode 100644
index 0000000..36c468f
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.cts.app;
+
+import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.playservicesstorage.PlayServicesStorage;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SdkSuppress;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.Assume;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutorService;
+
+// TODO(b/237116468): Remove SdkSuppress once AppSearchAttributionSource available for lower API
+// levels.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+public class AppSearchSessionGmsCtsTest extends AppSearchSessionCtsTestBase {
+
+ private boolean mIsGmsAvailable;
+ @Override
+ protected ListenableFuture<AppSearchSession> createSearchSessionAsync(@NonNull String dbName)
+ throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ ListenableFuture<AppSearchSession> appSearchSessionListenableFuture =
+ PlayServicesStorage.createSearchSessionAsync(
+ new PlayServicesStorage.SearchContext.Builder(context, dbName).build());
+ mIsGmsAvailable = GmsTestUtil.isGmsAvailable(appSearchSessionListenableFuture);
+
+ // isGmsAvailable returns false when GMSCore or GMSCore AppSearch module are unavailable on
+ // device. In this case we will not run the tests as they are expected to fail as the
+ // service they are calling is unavailable.
+ Assume.assumeTrue(mIsGmsAvailable);
+ return appSearchSessionListenableFuture;
+ }
+
+ @Override
+ protected ListenableFuture<AppSearchSession> createSearchSessionAsync(@NonNull String dbName,
+ @NonNull ExecutorService unused) throws Exception {
+ // Executor is not required for PlayServicesAppSearch.
+ return createSearchSessionAsync(dbName);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mIsGmsAvailable) {
+ super.tearDown();
+ }
+ }
+
+ @Override
+ @Test
+ public void testRfc822_unsupportedFeature_throwsException() {
+ // TODO(b/280463238): // TODO(b/280463238): KNOWN_ISSUE will be fixed in next
+ // play-services-appsearch drop.
+ // expected: tokenizerType is out of range of [0, 1] (too high)
+ // but was : tokenizerType is out of range of [%d, %d] (too high) [0, 1]
+ }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
index 56bf168..5e3e88a 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
@@ -160,63 +160,24 @@
// b/229770338 was fixed in Android T, this test will fail on S_V2 devices and below.
assumeTrue(BuildCompat.isAtLeastT());
super.testEmojiSnippet();
- }@Override
- @Test
- public void testSearchSuggestion() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
}
+ // TODO(b/256022027) Remove this overridden test once the change to setMaxJoinedResultCount
+ // is synced over into framework.
@Override
@Test
- public void testSearchSuggestion_namespaceFilter() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
+ public void testSimpleJoin() throws Exception { }
+ // TODO(b/256022027) Remove this overridden test once the change to rename
+ // `this.childrenScores()` to `this.childrenRankingSignals()` is synced to udc-dev.
@Override
@Test
- public void testSearchSuggestion_documentIdFilter() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
+ public void testQuery_invalidAdvancedRankingWithChildrenRankingSignals() throws Exception { }
+ // TODO(b/256022027) Remove this overridden test once the change to rename
+ // `this.childrenScores()` to `this.childrenRankingSignals()` is synced to udc-dev.
@Override
@Test
- public void testSearchSuggestion_differentPrefix() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
+ public void testQuery_advancedRankingWithJoin() throws Exception { }
- @Override
- @Test
- public void testSearchSuggestion_differentRankingStrategy() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
-
- @Override
- @Test
- public void testSearchSuggestion_removeDocument() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
-
- @Override
- @Test
- public void testSearchSuggestion_replacementDocument() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
-
- @Override
- @Test
- public void testSearchSuggestion_ignoreOperators() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
-
- @Override
- @Test
- public void testSearchSuggestion_schemaFilter() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
-
- @Override
- @Test
- public void testSearchSuggestion_twoInstances() throws Exception {
- // TODO(b/227356108) enable the test when suggestion is ready in platform.
- }
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
index dd71fc5..36cfac4 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
@@ -84,9 +84,10 @@
protected GlobalSearchSession mGlobalSearchSession;
protected abstract ListenableFuture<AppSearchSession> createSearchSessionAsync(
- @NonNull String dbName);
+ @NonNull String dbName) throws Exception;
- protected abstract ListenableFuture<GlobalSearchSession> createGlobalSearchSessionAsync();
+ protected abstract ListenableFuture<GlobalSearchSession> createGlobalSearchSessionAsync()
+ throws Exception;
@Before
public void setUp() throws Exception {
@@ -1837,15 +1838,22 @@
public void testGlobalQuery_propertyWeights() throws Exception {
assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SPEC_PROPERTY_WEIGHTS));
- // Schema registration
+ // RELEVANCE scoring depends on stats for the namespace+type of the scored document, namely
+ // the average document length. This average document length calculation is only updated
+ // when documents are added and when compaction runs. This means that old deleted
+ // documents of the same namespace and type combination *can* affect RELEVANCE scores
+ // through this channel.
+ // To avoid this, we use a unique namespace that will not be shared by any other test
+ // case or any other run of this test.
mDb1.setSchemaAsync(
new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build()).get();
mDb2.setSchemaAsync(
new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build()).get();
+ String namespace = "propertyWeightsNamespace" + System.currentTimeMillis();
// Put two documents in separate databases.
AppSearchEmail emailDb1 =
- new AppSearchEmail.Builder("namespace", "id1")
+ new AppSearchEmail.Builder(namespace, "id1")
.setCreationTimestampMillis(1000)
.setSubject("foo")
.build();
@@ -1853,7 +1861,7 @@
new PutDocumentsRequest.Builder()
.addGenericDocuments(emailDb1).build()));
AppSearchEmail emailDb2 =
- new AppSearchEmail.Builder("namespace", "id2")
+ new AppSearchEmail.Builder(namespace, "id2")
.setCreationTimestampMillis(1000)
.setBody("foo")
.build();
@@ -1868,6 +1876,7 @@
.setPropertyWeights(AppSearchEmail.SCHEMA_TYPE,
ImmutableMap.of("subject",
2.0, "body", 0.5))
+ .addFilterNamespaces(namespace)
.build());
List<SearchResult> globalResults = retrieveAllSearchResults(searchResults);
@@ -1889,6 +1898,7 @@
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
.setOrder(SearchSpec.ORDER_DESCENDING)
+ .addFilterNamespaces(namespace)
.build());
List<SearchResult> resultsWithoutWeights =
retrieveAllSearchResults(searchResultsWithoutWeights);
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java
new file mode 100644
index 0000000..c426c9b
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.cts.app;
+
+import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GlobalSearchSession;
+import androidx.appsearch.playservicesstorage.PlayServicesStorage;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SdkSuppress;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.Assume;
+import org.junit.Ignore;
+
+// TODO(b/237116468): Remove SdkSuppress once AppSearchAttributionSource available for lower API
+// levels.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+public class GlobalSearchSessionGmsCtsTest extends GlobalSearchSessionCtsTestBase {
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private boolean mIsGmsAvailable;
+ @Override
+ protected ListenableFuture<AppSearchSession> createSearchSessionAsync(@NonNull String dbName)
+ throws Exception {
+ ListenableFuture<AppSearchSession> searchSessionAsync =
+ PlayServicesStorage.createSearchSessionAsync(
+ new PlayServicesStorage.SearchContext.Builder(mContext, dbName).build());
+ mIsGmsAvailable = GmsTestUtil.isGmsAvailable(searchSessionAsync);
+
+ // isGmsAvailable returns false when GMSCore or GMSCore AppSearch module are unavailable on
+ // device. In this case we will not run the tests as they are expected to fail as the
+ // service they are calling is unavailable.
+ Assume.assumeTrue(mIsGmsAvailable);
+ return searchSessionAsync;
+ }
+
+ @Override
+ protected ListenableFuture<GlobalSearchSession> createGlobalSearchSessionAsync()
+ throws Exception {
+ ListenableFuture<GlobalSearchSession> globalSearchSessionAsync =
+ PlayServicesStorage.createGlobalSearchSessionAsync(
+ new PlayServicesStorage.GlobalSearchContext.Builder(mContext).build());
+ mIsGmsAvailable = GmsTestUtil.isGmsAvailable(globalSearchSessionAsync);
+
+ // isGmsAvailable returns false when GMSCore or GMSCore AppSearch module are unavailable on
+ // device. In this case we will not run the tests as they are expected to fail as the
+ // service they are calling is unavailable.
+ Assume.assumeTrue(mIsGmsAvailable);
+ return globalSearchSessionAsync;
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mIsGmsAvailable) {
+ super.tearDown();
+ }
+ }
+
+ @Override
+ @Ignore
+ public void testReportSystemUsage_ForbiddenFromNonSystem() {
+ // TODO(b/208654892) : ReportSystemUsage is not yet needed by any clients of GMSCore
+ // AppSearch, once there is a requirement by any of the clients this will be added.
+ }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GmsTestUtil.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GmsTestUtil.java
new file mode 100644
index 0000000..e968b9c
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GmsTestUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.cts.app;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.common.api.CommonStatusCodes;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
+
+/** Util class for GMSCore AppSearch related Cts tests. */
+public final class GmsTestUtil {
+
+ private GmsTestUtil() {}
+
+ /**
+ * This method returns false when GMSCore or GMSCore AppSearch module are unavailable on
+ * device.
+ */
+ public static boolean isGmsAvailable(
+ @NonNull ListenableFuture<?> sessionListenableFuture) throws Exception {
+ try {
+ sessionListenableFuture.get();
+ } catch (ExecutionException exception) {
+ if (exception.getCause() instanceof ApiException) {
+ ApiException apiException = (ApiException) exception.getCause();
+ if (apiException.getStatusCode() == CommonStatusCodes.API_NOT_CONNECTED) {
+ // GMSCore or GMSCore AppSearch Module not installed on device.
+ // TODO(b/280864281): Also handle the case when GMSCore and AppSearch
+ // module present but AppSearch dynamite module not present.
+ return false;
+ }
+ }
+
+ throw exception;
+ } catch (InterruptedException exception) {
+ throw exception;
+ }
+ return true;
+ }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
index 038cd56..371de2d 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
@@ -202,6 +202,52 @@
}
@Test
+ public void testGetTypePropertyWeightsWithAdvancedRanking() {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .setRankingStrategy("sum(this.propertyWeights())")
+ .setPropertyWeights("TypeA", ImmutableMap.of("property1", 1.0, "property2", 2.0))
+ .setPropertyWeights("TypeB", ImmutableMap.of("property1", 1.0, "property2"
+ + ".nested", 2.0))
+ .build();
+
+ Map<String, Map<String, Double>> typePropertyWeightsMap = searchSpec.getPropertyWeights();
+
+ assertThat(typePropertyWeightsMap.keySet())
+ .containsExactly("TypeA", "TypeB");
+ assertThat(typePropertyWeightsMap.get("TypeA")).containsExactly("property1", 1.0,
+ "property2", 2.0);
+ assertThat(typePropertyWeightsMap.get("TypeB")).containsExactly("property1", 1.0,
+ "property2.nested", 2.0);
+ }
+
+ @Test
+ public void testGetTypePropertyWeightPathsWithAdvancedRanking() {
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .setRankingStrategy("sum(this.propertyWeights())")
+ .setPropertyWeightPaths("TypeA",
+ ImmutableMap.of(new PropertyPath("property1"), 1.0,
+ new PropertyPath("property2"), 2.0))
+ .setPropertyWeightPaths("TypeB",
+ ImmutableMap.of(new PropertyPath("property1"), 1.0,
+ new PropertyPath("property2.nested"), 2.0))
+ .build();
+
+ Map<String, Map<PropertyPath, Double>> typePropertyWeightsMap =
+ searchSpec.getPropertyWeightPaths();
+
+ assertThat(typePropertyWeightsMap.keySet())
+ .containsExactly("TypeA", "TypeB");
+ assertThat(typePropertyWeightsMap.get("TypeA"))
+ .containsExactly(new PropertyPath("property1"), 1.0,
+ new PropertyPath("property2"), 2.0);
+ assertThat(typePropertyWeightsMap.get("TypeB"))
+ .containsExactly(new PropertyPath("property1"), 1.0,
+ new PropertyPath("property2.nested"), 2.0);
+ }
+
+ @Test
public void testSetPropertyWeights_nonPositiveWeight() {
SearchSpec.Builder searchSpecBuilder = new SearchSpec.Builder();
Map<String, Double> negativePropertyWeight = ImmutableMap.of("property", -1.0);
@@ -479,12 +525,13 @@
assertThat(e.getMessage()).isEqualTo("Attempting to rank based on joined documents, but"
+ " no JoinSpec provided");
+ JoinSpec joinSpec = new JoinSpec.Builder("childProp")
+ .setAggregationScoringStrategy(
+ JoinSpec.AGGREGATION_SCORING_SUM_RANKING_SIGNAL)
+ .build();
e = assertThrows(IllegalStateException.class, () -> new SearchSpec.Builder()
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
- .setJoinSpec(new JoinSpec.Builder("childProp")
- .setAggregationScoringStrategy(
- JoinSpec.AGGREGATION_SCORING_SUM_RANKING_SIGNAL)
- .build())
+ .setJoinSpec(joinSpec)
.build());
assertThat(e.getMessage()).isEqualTo("Aggregate scoring strategy has been set in the "
+ "nested JoinSpec, but ranking strategy is not "
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
index 5217017..69979c3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
@@ -16,14 +16,11 @@
package androidx.appsearch.cts.app;
-import static androidx.appsearch.app.AppSearchResult.RESULT_INVALID_ARGUMENT;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.appsearch.app.SearchSuggestionSpec;
-import androidx.appsearch.exceptions.AppSearchException;
import com.google.common.collect.ImmutableList;
@@ -68,12 +65,11 @@
@Test
public void testDocumentIdFilterMustMatchNamespaceFilter() throws Exception {
- AppSearchException e = assertThrows(AppSearchException.class,
+ IllegalStateException e = assertThrows(IllegalStateException.class,
() -> new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
.addFilterNamespaces("namespace1")
.addFilterDocumentIds("namespace2", ImmutableList.of("doc1"))
.build());
- assertThat(e.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
assertThat(e).hasMessageThat().contains("The namespace: namespace2 exists in the "
+ "document id filter but doesn't exist in the namespace filter.");
}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
index 004a07e..0033e71 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
@@ -26,10 +26,12 @@
import androidx.annotation.NonNull;
import androidx.appsearch.annotation.Document;
import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.DocumentClassFactoryRegistry;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.app.Migrator;
import androidx.appsearch.app.PackageIdentifier;
import androidx.appsearch.app.SetSchemaRequest;
+import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.testutil.AppSearchEmail;
import androidx.collection.ArrayMap;
@@ -374,7 +376,7 @@
}
-// @exportToFramework:startStrip()
+ // @exportToFramework:startStrip()
@Document
static class Card {
@Document.Namespace
@@ -473,7 +475,7 @@
.addRequiredPermissionsForDocumentClassVisibility(Card.class,
ImmutableSet.of(SetSchemaRequest.READ_SMS, SetSchemaRequest.READ_CALENDAR))
.addRequiredPermissionsForDocumentClassVisibility(Card.class,
- ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA));
+ ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA));
request = setSchemaRequestBuilder.build();
assertThat(request.getRequiredPermissionsForSchemaTypeVisibility())
@@ -833,4 +835,85 @@
assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getTokenizerType())
.isEqualTo(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_RFC822);
}
+
+ // @exportToFramework:startStrip()
+ @Document
+ static class Outer {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Middle mMiddle;
+ }
+
+ @Document
+ static class Middle {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Inner mInner;
+ }
+
+ @Document
+ static class Inner {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.StringProperty String mContents;
+ }
+
+ @Test
+ public void testNestedSchemas() throws AppSearchException {
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addDocumentClasses(Outer.class)
+ .setForceOverride(true).build();
+ DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
+
+ Set<AppSearchSchema> schemas = request.getSchemas();
+ assertThat(schemas).hasSize(3);
+ assertThat(schemas).contains(registry.getOrCreateFactory(Outer.class).getSchema());
+ assertThat(schemas).contains(registry.getOrCreateFactory(Middle.class).getSchema());
+ assertThat(schemas).contains(registry.getOrCreateFactory(Inner.class).getSchema());
+ }
+
+ @Document
+ static class Parent {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Person mPerson;
+ @Document.DocumentProperty Organization mOrganization;
+ }
+
+ @Document
+ static class Person {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Common mCommon;
+ }
+
+ @Document
+ static class Organization {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.DocumentProperty Common mCommon;
+ }
+
+ @Document
+ static class Common {
+ @Document.Id String mId;
+ @Document.Namespace String mNamespace;
+ @Document.StringProperty String mContents;
+ }
+
+
+ @Test
+ public void testNestedSchemasMultiplePaths() throws AppSearchException {
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addDocumentClasses(Parent.class)
+ .setForceOverride(true).build();
+ DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
+
+ Set<AppSearchSchema> schemas = request.getSchemas();
+ assertThat(schemas).hasSize(4);
+ assertThat(schemas).contains(registry.getOrCreateFactory(Common.class).getSchema());
+ assertThat(schemas).contains(registry.getOrCreateFactory(Organization.class).getSchema());
+ assertThat(schemas).contains(registry.getOrCreateFactory(Person.class).getSchema());
+ assertThat(schemas).contains(registry.getOrCreateFactory(Parent.class).getSchema());
+ }
+
+// @exportToFramework:endStrip()
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CanIgnoreReturnValue.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CanIgnoreReturnValue.java
new file mode 100644
index 0000000..7fa14d3
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/CanIgnoreReturnValue.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the return value of the annotated API is ignorable.
+ *
+ * @hide
+ */
+@Documented
+@Target({METHOD, CONSTRUCTOR, TYPE})
+@Retention(CLASS)
+public @interface CanIgnoreReturnValue {}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
index 04989eb..893a197 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
@@ -215,6 +215,28 @@
default AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
/**
+ * Configures how a property should be processed so that the document can be joined.
+ *
+ * <p>Properties configured with
+ * {@link AppSearchSchema.StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID} enable
+ * the documents to be joined with other documents that have the same qualified ID as the
+ * value of this field. (A qualified ID is a compact representation of the tuple <package
+ * name, database name, namespace, document ID> that uniquely identifies a document
+ * indexed in the AppSearch storage backend.) This property name can be specified as the
+ * child property expression in {@link androidx.appsearch.app.JoinSpec.Builder(String)} for
+ * join operations.
+ *
+ * <p>This attribute doesn't apply to properties of a repeated type (e.g., a list).
+ *
+ * <p>If not specified, defaults to
+ * {@link AppSearchSchema.StringPropertyConfig#JOINABLE_VALUE_TYPE_NONE}, which means the
+ * property can not be used in a child property expression to configure a
+ * {@link androidx.appsearch.app.JoinSpec.Builder(String)}.
+ */
+ @AppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType()
+ default AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
+
+ /**
* Configures whether this property must be specified for the document to be valid.
*
* <p>This attribute does not apply to properties of a repeated type (e.g. a list).
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
index 3cdf6ce..72dca86 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
@@ -17,6 +17,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArrayMap;
import androidx.core.util.Preconditions;
@@ -138,6 +139,7 @@
* @param value An optional value to associate with the successful result of the operation
* being performed.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
@NonNull
public Builder<KeyType, ValueType> setSuccess(
@@ -161,6 +163,7 @@
* {@link AppSearchResult#getResultCode}.
* @param errorMessage An optional string describing the reason or nature of the failure.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
@NonNull
public Builder<KeyType, ValueType> setFailure(
@@ -181,6 +184,7 @@
* identifier from the input like an ID or name.
* @param result The result to associate with the key.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
@NonNull
public Builder<KeyType, ValueType> setResult(
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
index 31b0a88..3ae1a52 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
@@ -52,6 +52,7 @@
RESULT_NOT_FOUND,
RESULT_INVALID_SCHEMA,
RESULT_SECURITY_ERROR,
+ RESULT_DENIED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -95,6 +96,14 @@
/** The caller requested an operation it does not have privileges for. */
public static final int RESULT_SECURITY_ERROR = 8;
+ /**
+ * The requested operation is denied for the caller. This error is logged and returned for
+ * denylist rejections.
+ * <!--@exportToFramework:hide-->
+ */
+ // TODO(b/279047435): unhide this the next time we can make API changes
+ public static final int RESULT_DENIED = 9;
+
private final @ResultCode int mResultCode;
@Nullable private final ValueType mResultValue;
@Nullable private final String mErrorMessage;
@@ -114,7 +123,8 @@
}
/** Returns one of the {@code RESULT} constants defined in {@link AppSearchResult}. */
- public @ResultCode int getResultCode() {
+ @ResultCode
+ public int getResultCode() {
return mResultCode;
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
index 8a05b92..64ae97a 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
@@ -23,6 +23,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.exceptions.IllegalSchemaException;
import androidx.appsearch.util.BundleUtil;
import androidx.appsearch.util.IndentingStringBuilder;
@@ -112,7 +113,7 @@
builder.append("}");
}
- /** Returns the name of this schema type, e.g. Email. */
+ /** Returns the name of this schema type, such as Email. */
@NonNull
public String getSchemaType() {
return mBundle.getString(SCHEMA_TYPE_FIELD, "");
@@ -172,6 +173,7 @@
}
/** Adds a property to the given type. */
+ @CanIgnoreReturnValue
@NonNull
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
Preconditions.checkNotNull(propertyConfig);
@@ -215,10 +217,12 @@
/**
* Physical data-types of the contents of the property.
+ *
+ * <p>NOTE: The integer values of these constants must match the proto enum constants in
+ * com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
+ *
* @hide
*/
- // NOTE: The integer values of these constants must match the proto enum constants in
- // com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
@IntDef(value = {
DATA_TYPE_STRING,
DATA_TYPE_LONG,
@@ -262,10 +266,12 @@
/**
* The cardinality of the property (whether it is required, optional or repeated).
+ *
+ * <p>NOTE: The integer values of these constants must match the proto enum constants in
+ * com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
+ *
* @hide
*/
- // NOTE: The integer values of these constants must match the proto enum constants in
- // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
@IntDef(value = {
CARDINALITY_REPEATED,
CARDINALITY_OPTIONAL,
@@ -371,18 +377,20 @@
}
/**
- * Returns the type of data the property contains (e.g. string, int, bytes, etc).
+ * Returns the type of data the property contains (such as string, int, bytes, etc).
*
* @hide
*/
- public @DataType int getDataType() {
+ @DataType
+ public int getDataType() {
return mBundle.getInt(DATA_TYPE_FIELD, -1);
}
/**
* Returns the cardinality of the property (whether it is optional, required or repeated).
*/
- public @Cardinality int getCardinality() {
+ @Cardinality
+ public int getCardinality() {
return mBundle.getInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL);
}
@@ -446,6 +454,7 @@
private static final String INDEXING_TYPE_FIELD = "indexingType";
private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
private static final String JOINABLE_VALUE_TYPE_FIELD = "joinableValueType";
+ private static final String DELETION_PROPAGATION_FIELD = "deletionPropagation";
/**
* Encapsulates the configurations on how AppSearch should query/index these terms.
@@ -466,7 +475,7 @@
* Content in this property should only be returned for queries matching the exact tokens
* appearing in this property.
*
- * <p>Ex. A property with "fool" should NOT match a query for "foo".
+ * <p>For example, a property with "fool" should NOT match a query for "foo".
*/
public static final int INDEXING_TYPE_EXACT_TERMS = 1;
@@ -474,16 +483,18 @@
* Content in this property should be returned for queries that are either exact matches or
* query matches of the tokens appearing in this property.
*
- * <p>Ex. A property with "fool" <b>should</b> match a query for "foo".
+ * <p>For example, a property with "fool" <b>should</b> match a query for "foo".
*/
public static final int INDEXING_TYPE_PREFIXES = 2;
/**
* Configures how tokens should be extracted from this property.
+ *
+ * <p>NOTE: The integer values of these constants must match the proto enum constants in
+ * com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
+ *
* @hide
*/
- // NOTE: The integer values of these constants must match the proto enum constants in
- // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
@IntDef(value = {
TOKENIZER_TYPE_NONE,
TOKENIZER_TYPE_PLAIN,
@@ -506,8 +517,8 @@
* this property based on word breaks. Segments of whitespace and punctuation are not
* considered tokens.
*
- * <p>Ex. A property with "foo bar. baz." will produce tokens for "foo", "bar" and "baz".
- * The segments " " and "." will not be considered tokens.
+ * <p>For example, a property with "foo bar. baz." will produce tokens for "foo", "bar" and
+ * "baz". The segments " " and "." will not be considered tokens.
*
* <p>It is only valid for tokenizer_type to be 'PLAIN' if {@link #getIndexingType} is
* {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
@@ -519,7 +530,7 @@
* values that are tokenized using this type. Therefore, the output token is equivalent
* to the raw string value.
*
- * <p>Ex. A property with "Hello, world!" will produce the token "Hello, world!",
+ * <p>For example, a property with "Hello, world!" will produce the token "Hello, world!",
* preserving punctuation and capitalization, and not creating separate tokens between the
* space.
*
@@ -537,8 +548,8 @@
* Tokenization for emails. This value indicates that tokens should be extracted from
* this property based on email structure.
*
- * <p>Ex. A property with "alex.sav@google.com" will produce tokens for "alex", "sav",
- * "alex.sav", "google", "com", and "alexsav@google.com"
+ * <p>For example, a property with "alex.sav@google.com" will produce tokens for "alex",
+ * "sav", "alex.sav", "google", "com", and "alexsav@google.com"
*
* <p>It is only valid for tokenizer_type to be 'RFC822' if {@link #getIndexingType} is
* {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
@@ -592,29 +603,44 @@
}
/** Returns how the property is indexed. */
- public @IndexingType int getIndexingType() {
+ @IndexingType
+ public int getIndexingType() {
return mBundle.getInt(INDEXING_TYPE_FIELD);
}
/** Returns how this property is tokenized (split into words). */
- public @TokenizerType int getTokenizerType() {
+ @TokenizerType
+ public int getTokenizerType() {
return mBundle.getInt(TOKENIZER_TYPE_FIELD);
}
/**
* Returns how this property is going to be used to join documents from other schema types.
*/
- public @JoinableValueType int getJoinableValueType() {
+ @JoinableValueType
+ public int getJoinableValueType() {
return mBundle.getInt(JOINABLE_VALUE_TYPE_FIELD, JOINABLE_VALUE_TYPE_NONE);
}
+ /**
+ * Returns whether or not documents in this schema should be deleted when the document
+ * referenced by this field is deleted.
+ *
+ * @see JoinSpec
+ * @<!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()hide-->
+ */
+ public boolean getDeletionPropagation() {
+ return mBundle.getBoolean(DELETION_PROPAGATION_FIELD, false);
+ }
+
/** Builder for {@link StringPropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
- private @IndexingType int mIndexingType = INDEXING_TYPE_NONE;
- private @TokenizerType int mTokenizerType = TOKENIZER_TYPE_NONE;
- private @JoinableValueType int mJoinableValueType = JOINABLE_VALUE_TYPE_NONE;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+ @IndexingType private int mIndexingType = INDEXING_TYPE_NONE;
+ @TokenizerType private int mTokenizerType = TOKENIZER_TYPE_NONE;
+ @JoinableValueType private int mJoinableValueType = JOINABLE_VALUE_TYPE_NONE;
+ private boolean mDeletionPropagation = false;
/** Creates a new {@link StringPropertyConfig.Builder}. */
public Builder(@NonNull String propertyName) {
@@ -622,11 +648,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public StringPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -643,6 +670,7 @@
* {@link StringPropertyConfig#INDEXING_TYPE_NONE}, so that it cannot be matched by
* queries.
*/
+ @CanIgnoreReturnValue
@NonNull
public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
Preconditions.checkArgumentInRange(
@@ -658,10 +686,11 @@
* {@link StringPropertyConfig#TOKENIZER_TYPE_NONE}, so that it is not tokenized.
*
* <p>This method must be called with a value other than
- * {@link StringPropertyConfig#TOKENIZER_TYPE_NONE} if the property is indexed (i.e.
+ * {@link StringPropertyConfig#TOKENIZER_TYPE_NONE} if the property is indexed (that is,
* if {@link #setIndexingType} has been called with a value other than
* {@link StringPropertyConfig#INDEXING_TYPE_NONE}).
*/
+ @CanIgnoreReturnValue
@NonNull
public StringPropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
Preconditions.checkArgumentInRange(
@@ -675,7 +704,10 @@
*
* <p>If this method is not called, the default joinable value type is
* {@link StringPropertyConfig#JOINABLE_VALUE_TYPE_NONE}, so that it is not joinable.
+ *
+ * <p>At most, 64 properties can be set as joinable per schema.
*/
+ @CanIgnoreReturnValue
@NonNull
public StringPropertyConfig.Builder setJoinableValueType(
@JoinableValueType int joinableValueType) {
@@ -689,6 +721,25 @@
}
/**
+ * Configures whether or not documents in this schema will be removed when the document
+ * referred to by this property is deleted.
+ *
+ * <p> Requires that a joinable value type is set.
+ * @<!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()hide-->
+ */
+ @SuppressWarnings("MissingGetterMatchingBuilder") // getDeletionPropagation
+ @NonNull
+ // @exportToFramework:startStrip()
+ @RequiresFeature(
+ enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+ name = Features.SCHEMA_SET_DELETION_PROPAGATION)
+ // @exportToFramework:endStrip()
+ public Builder setDeletionPropagation(boolean deletionPropagation) {
+ mDeletionPropagation = deletionPropagation;
+ return this;
+ }
+
+ /**
* Constructs a new {@link StringPropertyConfig} from the contents of this builder.
*/
@NonNull
@@ -704,6 +755,9 @@
if (mJoinableValueType == JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
Preconditions.checkState(mCardinality != CARDINALITY_REPEATED, "Cannot set "
+ "JOINABLE_VALUE_TYPE_QUALIFIED_ID with CARDINALITY_REPEATED.");
+ } else {
+ Preconditions.checkState(!mDeletionPropagation, "Cannot set deletion "
+ + "propagation without setting a joinable value type");
}
Bundle bundle = new Bundle();
bundle.putString(NAME_FIELD, mPropertyName);
@@ -712,6 +766,7 @@
bundle.putInt(INDEXING_TYPE_FIELD, mIndexingType);
bundle.putInt(TOKENIZER_TYPE_FIELD, mTokenizerType);
bundle.putInt(JOINABLE_VALUE_TYPE_FIELD, mJoinableValueType);
+ bundle.putBoolean(DELETION_PROPAGATION_FIELD, mDeletionPropagation);
return new StringPropertyConfig(bundle);
}
}
@@ -793,7 +848,7 @@
* Content in this property will be indexed and can be fetched via numeric search range
* query.
*
- * <p>Ex. A property with 1024 should match numeric search range query [0, 2000].
+ * <p>For example, a property with 1024 should match numeric search range query [0, 2000].
*/
// @exportToFramework:startStrip()
@RequiresFeature(
@@ -807,15 +862,16 @@
}
/** Returns how the property is indexed. */
- public @IndexingType int getIndexingType() {
+ @IndexingType
+ public int getIndexingType() {
return mBundle.getInt(INDEXING_TYPE_FIELD, INDEXING_TYPE_NONE);
}
/** Builder for {@link LongPropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
- private @IndexingType int mIndexingType = INDEXING_TYPE_NONE;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+ @IndexingType private int mIndexingType = INDEXING_TYPE_NONE;
/** Creates a new {@link LongPropertyConfig.Builder}. */
public Builder(@NonNull String propertyName) {
@@ -823,11 +879,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public LongPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -844,6 +901,7 @@
* {@link LongPropertyConfig#INDEXING_TYPE_NONE}, so that it will not be indexed
* and cannot be matched by queries.
*/
+ @CanIgnoreReturnValue
@NonNull
public LongPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
Preconditions.checkArgumentInRange(
@@ -895,7 +953,7 @@
/** Builder for {@link DoublePropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
/** Creates a new {@link DoublePropertyConfig.Builder}. */
public Builder(@NonNull String propertyName) {
@@ -903,11 +961,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public DoublePropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -938,7 +997,7 @@
/** Builder for {@link BooleanPropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
/** Creates a new {@link BooleanPropertyConfig.Builder}. */
public Builder(@NonNull String propertyName) {
@@ -946,11 +1005,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public BooleanPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -981,7 +1041,7 @@
/** Builder for {@link BytesPropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
/** Creates a new {@link BytesPropertyConfig.Builder}. */
public Builder(@NonNull String propertyName) {
@@ -989,11 +1049,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public BytesPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -1047,7 +1108,7 @@
public static final class Builder {
private final String mPropertyName;
private final String mSchemaType;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
+ @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
private boolean mShouldIndexNestedProperties = false;
/**
@@ -1066,11 +1127,12 @@
}
/**
- * The cardinality of the property (whether it is optional, required or repeated).
+ * Sets the cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is
* {@link PropertyConfig#CARDINALITY_OPTIONAL}.
*/
+ @CanIgnoreReturnValue
@SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
@NonNull
public DocumentPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
@@ -1087,6 +1149,7 @@
* <p>If false, the nested document's properties are not indexed regardless of its own
* schema.
*/
+ @CanIgnoreReturnValue
@NonNull
public DocumentPropertyConfig.Builder setShouldIndexNestedProperties(
boolean indexNestedProperties) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
index 16098ebc..e9679b7 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
import com.google.common.util.concurrent.ListenableFuture;
@@ -208,17 +207,14 @@
*
* <p>Search suggestions with the multiple term {@code suggestionQueryExpression} "org t", the
* suggested result will be "org term1" - The last token is completed by the suggested
- * String, even if it won't return any result.
+ * String.
*
- * <p>Search suggestions with operators. All operators will be considered as a normal term.
- * <ul>
- * <li>Search suggestions with the {@code suggestionQueryExpression} "term1 OR", the
- * suggested result is "term1 org".
- * <li>Search suggestions with the {@code suggestionQueryExpression} "term3 OR t", the
- * suggested result is "term3 OR term1".
- * <li>Search suggestions with the {@code suggestionQueryExpression} "content:t", the
- * suggested result is empty. It cannot find a document that contains the term "content:t".
- * </ul>
+ * <p>Operators in {@link #search} are supported.
+ * <p><b>NOTE:</b> Exclusion and Grouped Terms in the last term is not supported.
+ * <p>example: "apple -f": This Api will throw an
+ * {@link androidx.appsearch.exceptions.AppSearchException} with
+ * {@link AppSearchResult#RESULT_INVALID_ARGUMENT}.
+ * <p>example: "apple (f)": This Api will return an empty results.
*
* <p>Invalid example: All these input {@code suggestionQueryExpression} don't have a valid
* last token, AppSearch will return an empty result list.
@@ -229,10 +225,6 @@
* <li>"f " - Ending in trailing space.
* </ul>
*
- * <p>Property restrict query like "subject:f" is not supported in suggestion API. It will
- * return suggested String starting with "f" even if the term appears other than "subject"
- * property.
- *
* @param suggestionQueryExpression the non empty query string to search suggestions
* @param searchSuggestionSpec spec for setting document filters
* @return The pending result of performing this operation which resolves to a List of
@@ -241,15 +233,8 @@
* in {@link #search}.
*
* @see #search(String, SearchSpec)
- * <!--@exportToFramework:ifJetpack()-->@hide<!--@exportToFramework:else()-->
*/
- //TODO(b/227356108) Change the comment in this API after fix following issues.
- // 1: support property restrict tokenization, Example: [subject:car] will return ["cart",
- // "carburetor"] if AppSearch has documents contain those terms.
- // 2: support multiple terms, Example: [bar f] will return suggestions [bar foo] that could
- // be used to retrieve documents that contain both terms "bar" and "foo".
@NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
ListenableFuture<List<SearchSuggestionResult>> searchSuggestionAsync(
@NonNull String suggestionQueryExpression,
@NonNull SearchSuggestionSpec searchSuggestionSpec);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/DocumentClassFactory.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/DocumentClassFactory.java
index bd03221..fd5987e 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/DocumentClassFactory.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/DocumentClassFactory.java
@@ -19,6 +19,8 @@
import androidx.annotation.NonNull;
import androidx.appsearch.exceptions.AppSearchException;
+import java.util.List;
+
/**
* An interface for factories which can convert between instances of classes annotated with
* \@{@link androidx.appsearch.annotation.Document} and instances of {@link GenericDocument}.
@@ -39,6 +41,13 @@
AppSearchSchema getSchema() throws AppSearchException;
/**
+ * Returns dependent document classes used in this document class. This is useful as we can set
+ * dependent schemas without requiring clients to explicitly set all dependent schemas.
+ */
+ @NonNull
+ List<Class<?>> getNestedDocumentClasses() throws AppSearchException;
+
+ /**
* Converts an instance of the class annotated with
* \@{@link androidx.appsearch.annotation.Document} into a
* {@link androidx.appsearch.app.GenericDocument}.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java
new file mode 100644
index 0000000..fc3218f
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+/**
+ * A class that encapsulates all feature constants that are accessible in AppSearch framework.
+ *
+ * <p>All fields in this class is referring in {@link Features}. If you add/remove any field in this
+ * class, you should also change {@link Features}.
+ * @see Features
+ * @hide
+ */
+public interface FeatureConstants {
+ /** Feature constants for {@link Features#NUMERIC_SEARCH}. */
+ String NUMERIC_SEARCH = "NUMERIC_SEARCH";
+
+ /** Feature constants for {@link Features#VERBATIM_SEARCH}. */
+ String VERBATIM_SEARCH = "VERBATIM_SEARCH";
+
+ /** Feature constants for {@link Features#LIST_FILTER_QUERY_LANGUAGE}. */
+ String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
index 4a4af6c..4686ea7 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
@@ -26,6 +26,8 @@
* the feature will be available forever on that AppSearch storage implementation, at that
* Android API level, on that device.
*/
+
+// @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/Features.java)
public interface Features {
/**
@@ -78,7 +80,7 @@
* {@link AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_RANGE} and all other numeric search
* features.
*/
- String NUMERIC_SEARCH = "NUMERIC_SEARCH";
+ String NUMERIC_SEARCH = FeatureConstants.NUMERIC_SEARCH;
/**
* Feature for {@link #isFeatureSupported(String)}. This feature covers
@@ -88,7 +90,7 @@
*
* <p>Ex. '"foo/bar" OR baz' will ensure that 'foo/bar' is treated as a single 'verbatim' token.
*/
- String VERBATIM_SEARCH = "VERBATIM_SEARCH";
+ String VERBATIM_SEARCH = FeatureConstants.VERBATIM_SEARCH;
/**
* Feature for {@link #isFeatureSupported(String)}. This feature covers the
@@ -115,7 +117,13 @@
* for example, the query "(subject:foo OR body:foo) (subject:bar OR body:bar)"
* could be rewritten as "termSearch(\"foo bar\", createList(\"subject\", \"bar\"))"
*/
- String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+ String LIST_FILTER_QUERY_LANGUAGE = FeatureConstants.LIST_FILTER_QUERY_LANGUAGE;
+
+ /**
+ * Feature for {@link #isFeatureSupported(String)}. This feature covers
+ * {@link SearchSpec#GROUPING_TYPE_PER_SCHEMA}
+ */
+ String SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA = "SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA";
/** Feature for {@link #isFeatureSupported(String)}. This feature covers
* {@link SearchSpec.Builder#setPropertyWeights}.
@@ -136,6 +144,18 @@
String JOIN_SPEC_AND_QUALIFIED_ID = "JOIN_SPEC_AND_QUALIFIED_ID";
/**
+ * Feature for {@link #isFeatureSupported(String)}. This feature covers
+ * {@link AppSearchSession#searchSuggestionAsync}.
+ */
+ String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
+
+ /**
+ * Feature for {@link #isFeatureSupported(String)}. This feature covers
+ * {@link AppSearchSchema.StringPropertyConfig.Builder#setDeletionPropagation}.
+ */
+ String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
+
+ /**
* Returns whether a feature is supported at run-time. Feature support depends on the
* feature in question, the AppSearch backend being used and the Android version of the
* device.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
index 5dd5a6c..992bb0d 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
@@ -25,6 +25,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.annotation.Document;
import androidx.appsearch.app.PropertyPath.PathSegment;
import androidx.appsearch.exceptions.AppSearchException;
@@ -426,7 +427,7 @@
// paths we return the bare document Bundle in this particular case.
Parcelable[] bundles = (Parcelable[]) currentElementValue;
if (index < bundles.length) {
- extractedValue = (Bundle) bundles[index];
+ extractedValue = bundles[index];
}
} else {
throw new IllegalStateException("Unsupported value type: "
@@ -1127,6 +1128,7 @@
* <p>The number of namespaces per app should be kept small for efficiency reasons.
* <!--@exportToFramework:hide-->
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setNamespace(@NonNull String namespace) {
Preconditions.checkNotNull(namespace);
@@ -1142,6 +1144,7 @@
* <p>Document IDs are unique within a namespace.
* <!--@exportToFramework:hide-->
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setId(@NonNull String id) {
Preconditions.checkNotNull(id);
@@ -1157,6 +1160,7 @@
* {@link AppSearchSchema} object previously provided to {@link AppSearchSession#setSchemaAsync}.
* <!--@exportToFramework:hide-->
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setSchemaType(@NonNull String schemaType) {
Preconditions.checkNotNull(schemaType);
@@ -1177,7 +1181,9 @@
* <p>Any non-negative integer can be used a score. By default, scores are set to 0.
*
* @param score any non-negative {@code int} representing the document's score.
+ * @throws IllegalArgumentException if the score is negative.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
if (score < 0) {
@@ -1198,6 +1204,7 @@
*
* @param creationTimestampMillis a creation timestamp in milliseconds.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setCreationTimestampMillis(
/*@exportToFramework:CurrentTimeMillisLong*/ long creationTimestampMillis) {
@@ -1219,7 +1226,9 @@
* called.
*
* @param ttlMillis a non-negative duration in milliseconds.
+ * @throws IllegalArgumentException if ttlMillis is negative.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setTtlMillis(long ttlMillis) {
if (ttlMillis < 0) {
@@ -1241,6 +1250,7 @@
* @throws IllegalArgumentException if no values are provided, or if a passed in
* {@code String} is {@code null} or "".
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
Preconditions.checkNotNull(name);
@@ -1260,6 +1270,7 @@
* @param values the {@code boolean} values of the property.
* @throws IllegalArgumentException if the name is empty or {@code null}.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
Preconditions.checkNotNull(name);
@@ -1279,6 +1290,7 @@
* @param values the {@code long} values of the property.
* @throws IllegalArgumentException if the name is empty or {@code null}.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
Preconditions.checkNotNull(name);
@@ -1298,6 +1310,7 @@
* @param values the {@code double} values of the property.
* @throws IllegalArgumentException if the name is empty or {@code null}.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
Preconditions.checkNotNull(name);
@@ -1317,6 +1330,7 @@
* @throws IllegalArgumentException if no values are provided, or if a passed in
* {@code byte[]} is {@code null}, or if name is empty.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
Preconditions.checkNotNull(name);
@@ -1338,6 +1352,7 @@
* {@link GenericDocument} is {@code null}, or if name
* is empty.
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType setPropertyDocument(
@NonNull String name, @NonNull GenericDocument... values) {
@@ -1356,6 +1371,7 @@
* @param name The name of the property to clear.
* <!--@exportToFramework:hide-->
*/
+ @CanIgnoreReturnValue
@NonNull
public BuilderType clearProperty(@NonNull String name) {
Preconditions.checkNotNull(name);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
index d7ff30b..2bee987 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArrayMap;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -134,6 +135,7 @@
}
/** Adds one or more document IDs to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIds(@NonNull String... ids) {
Preconditions.checkNotNull(ids);
@@ -142,6 +144,7 @@
}
/** Adds a collection of IDs to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIds(@NonNull Collection<String> ids) {
Preconditions.checkNotNull(ids);
@@ -166,6 +169,7 @@
*
* @see SearchSpec.Builder#addProjectionPaths
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addProjection(
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
@@ -197,6 +201,7 @@
*
* @see SearchSpec.Builder#addProjectionPaths
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addProjectionPaths(
@NonNull String schemaType, @NonNull Collection<PropertyPath> propertyPaths) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
index 3475576..e820972 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
@@ -24,6 +24,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArrayMap;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -48,14 +49,14 @@
/**
* This Set contains all schemas that are not displayed by the system. All values in the set are
* prefixed with the package-database prefix. We do lazy fetch, the object will be created
- * when the user first time fetch it.
+ * when you first time fetch it.
*/
@Nullable
private Set<String> mSchemasNotDisplayedBySystem;
/**
* This map contains all schemas and {@link PackageIdentifier} that has access to the schema.
* All keys in the map are prefixed with the package-database prefix. We do lazy fetch, the
- * object will be created when the user first time fetch it.
+ * object will be created when you first time fetch it.
*/
@Nullable
private Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
@@ -63,7 +64,7 @@
/**
* This map contains all schemas and Android Permissions combinations that are required to
* access the schema. All keys in the map are prefixed with the package-database prefix. We
- * do lazy fetch, the object will be created when the user first time fetch it.
+ * do lazy fetch, the object will be created when you first time fetch it.
* The Map is constructed in ANY-ALL cases. The querier could read the {@link GenericDocument}
* objects under the {@code schemaType} if they holds ALL required permissions of ANY
* combinations.
@@ -276,6 +277,7 @@
*
* <p>Default version is 0
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setVersion(@IntRange(from = 0) int version) {
resetIfBuilt();
@@ -284,6 +286,7 @@
}
/** Adds one {@link AppSearchSchema} to the schema list. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addSchema(@NonNull AppSearchSchema schema) {
Preconditions.checkNotNull(schema);
@@ -300,6 +303,7 @@
* {@link GetSchemaResponse}, which won't be displayed by system.
*/
// Getter getSchemaTypesNotDisplayedBySystem returns plural objects.
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addSchemaTypeNotDisplayedBySystem(@NonNull String schemaType) {
@@ -331,6 +335,7 @@
* schema type.
*/
// Getter getSchemaTypesVisibleToPackages returns a map contains all schema types.
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setSchemaTypeVisibleToPackages(
@@ -378,6 +383,7 @@
* the given schema.
*/
// Getter getRequiredPermissionsForSchemaTypeVisibility returns a map for all schemaTypes.
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setRequiredPermissionsForSchemaTypeVisibility(
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
index b5dcd55..eeee360 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
@@ -21,6 +21,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.core.util.Preconditions;
import java.lang.annotation.Retention;
@@ -32,9 +33,15 @@
* <p> Joins are only possible for matching on the qualified id of an outer document and a
* property value within a subquery document. In the subquery documents, these values may be
* referred to with a property path such as "email.recipient.id" or "entityId" or a property
- * expression. One such property expression is {@link #QUALIFIED_ID}, which refers to the
+ * expression. One such property expression is "this.qualifiedId()", which refers to the
* document's combined package, database, namespace, and id.
*
+ * <p>Note that in order for perform the join, the property referred to by
+ * {@link #getChildPropertyExpression} has to be a property with
+ * {@link AppSearchSchema.StringPropertyConfig#getJoinableValueType} set to
+ * {@link AppSearchSchema.StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID}. Otherwise no
+ * documents will be joined to any {@link SearchResult}.
+ *
* <p> Take these outer query and subquery results for example:
*
* <pre>{@code
@@ -69,19 +76,47 @@
* does not equal the qualified id of the outer query result. As such, subquery result 1 will not be
* joined to the outer query result.
*
+ * <p> It's possible to define an advanced ranking strategy in the nested {@link SearchSpec} and
+ * also use {@link SearchSpec#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE} in the outer
+ * {@link SearchSpec}. In this case, the parents will be ranked based on an aggregation, such as
+ * the sum, of the signals calculated by scoring the joined documents with the advanced ranking
+ * strategy.
+ *
* <p> In terms of scoring, if {@link SearchSpec#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE} is set in
* {@link SearchSpec#getRankingStrategy}, the scores of the outer SearchResults can be influenced
* by the ranking signals of the subquery results. For example, if the
- * {@link JoinSpec#getAggregationScoringStrategy} is set to
+ * {@link JoinSpec#getAggregationScoringStrategy} is set to:
+ * <ul><li>
* {@link JoinSpec#AGGREGATION_SCORING_MIN_RANKING_SIGNAL}, the ranking signal of the outer
* {@link SearchResult} will be set to the minimum of the ranking signals of the subquery results.
- * In this case, it will be the minimum of 2 and 3, which is 2. If the
- * {@link JoinSpec#getAggregationScoringStrategy} is set to
+ * In this case, it will be the minimum of 2 and 3, which is 2.
+ * </li>
+ * <li>
+ * {@link JoinSpec#AGGREGATION_SCORING_MAX_RANKING_SIGNAL}, the ranking signal of the outer
+ * {@link SearchResult} will be 3.
+ * </li>
+ * <li>
+ * {@link JoinSpec#AGGREGATION_SCORING_AVG_RANKING_SIGNAL}, the ranking signal of the outer
+ * {@link SearchResult} will be 2.5.
+ * </li>
+ * <li>
+ * {@link JoinSpec#AGGREGATION_SCORING_RESULT_COUNT}, the ranking signal of the outer
+ * {@link SearchResult} will be 2 as there are two joined results.
+ * </li>
+ * <li>
+ * {@link JoinSpec#AGGREGATION_SCORING_SUM_RANKING_SIGNAL}, the ranking signal of the outer
+ * {@link SearchResult} will be 5, the sum of 2 and 3.
+ * </li>
+ * <li>
* {@link JoinSpec#AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL}, the ranking signal of the outer
* {@link SearchResult} will stay as it is.
+ * </li>
+ * </ul>
+ *
+ * <p> Referring to "this.childrenRankingSignals()" in the ranking signal of the outer query will
+ * return the signals calculated by scoring the joined documents using the scoring strategy in the
+ * nested {@link SearchSpec}, as in {@link SearchResult#getRankingSignal}.
*/
-// TODO(b/256022027): Update javadoc once "Joinable"/"qualifiedId" type is added to reflect the
-// fact that childPropertyExpression has to point to property of that type.
public final class JoinSpec {
static final String NESTED_QUERY = "nestedQuery";
static final String NESTED_SEARCH_SPEC = "nestedSearchSpec";
@@ -97,7 +132,10 @@
*
* <p> For instance, if a document with an id of "id1" exists in the namespace "ns" within
* the database "db" created by package "pkg", this would evaluate to "pkg$db/ns#id1".
+ *
+ * <!--@exportToFramework:hide-->
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String QUALIFIED_ID = "this.qualifiedId()";
/**
@@ -177,8 +215,8 @@
}
/**
- * Returns the max amount of {@link SearchResult} objects that will be joined to the parent
- * document, with a default of 10 SearchResults.
+ * Returns the max amount of {@link SearchResult} objects to return with the parent document,
+ * with a default of 10 SearchResults.
*/
public int getMaxJoinedResultCount() {
return mBundle.getInt(MAX_JOINED_RESULT_COUNT);
@@ -204,7 +242,8 @@
*
* @see SearchSpec#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE
*/
- public @AggregationScoringStrategy int getAggregationScoringStrategy() {
+ @AggregationScoringStrategy
+ public int getAggregationScoringStrategy() {
return mBundle.getInt(AGGREGATION_SCORING_STRATEGY);
}
@@ -218,7 +257,7 @@
private SearchSpec mNestedSearchSpec = EMPTY_SEARCH_SPEC;
private final String mChildPropertyExpression;
private int mMaxJoinedResultCount = DEFAULT_MAX_JOINED_RESULT_COUNT;
- private @AggregationScoringStrategy int mAggregationScoringStrategy =
+ @AggregationScoringStrategy private int mAggregationScoringStrategy =
AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL;
/**
@@ -251,7 +290,15 @@
}
/**
- * Further filters the documents being joined.
+ * Sets the query and the SearchSpec for the documents being joined. This will score and
+ * rank the joined documents as well as filter the joined documents.
+ *
+ * <p>If {@link SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE} is set in
+ * the outer {@link SearchSpec}, the resulting signals will be used to rank the parent
+ * documents. Note that the aggregation strategy also needs to be set with
+ * {@link JoinSpec.Builder#setAggregationScoringStrategy}, otherwise the default will be
+ * {@link JoinSpec#AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL}, which will just use
+ * the parent documents ranking signal.
*
* <p> If this method is never called, {@link JoinSpec#getNestedQuery} will return an empty
* string, meaning we will join with every possible document that matches the equality
@@ -262,6 +309,7 @@
*/
@SuppressWarnings("MissingGetterMatchingBuilder")
// See getNestedQuery & getNestedSearchSpec
+ @CanIgnoreReturnValue
@NonNull
public Builder setNestedSearch(@NonNull String nestedQuery,
@NonNull SearchSpec nestedSearchSpec) {
@@ -274,9 +322,15 @@
}
/**
- * Sets the max amount of {@link SearchResults} to join to the parent document, with a
+ * Sets the max amount of {@link SearchResults} to return with the parent document, with a
* default of 10 SearchResults.
+ *
+ * <p>This does NOT limit the number of results that are joined with the parent
+ * document for scoring. This means that, when set, only a maximum of
+ * {@code maxJoinedResultCount} results will be returned with each parent document, but
+ * all results that are joined with a parent will factor into the score.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setMaxJoinedResultCount(int maxJoinedResultCount) {
mMaxJoinedResultCount = maxJoinedResultCount;
@@ -292,6 +346,7 @@
*
* @see SearchSpec#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setAggregationScoringStrategy(
@AggregationScoringStrategy int aggregationScoringStrategy) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
index dff69b2..6edf85e 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.core.util.Preconditions;
@@ -58,6 +59,7 @@
private boolean mBuilt = false;
/** Adds one or more {@link GenericDocument} objects to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents);
@@ -66,6 +68,7 @@
}
/** Adds a collection of {@link GenericDocument} objects to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) {
@@ -87,6 +90,7 @@
*/
// Merged list available from getGenericDocuments()
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addDocuments(@NonNull Object... documents) throws AppSearchException {
Preconditions.checkNotNull(documents);
@@ -105,6 +109,7 @@
*/
// Merged list available from getGenericDocuments()
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addDocuments(@NonNull Collection<?> documents) throws AppSearchException {
Preconditions.checkNotNull(documents);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
index 38be17a..40cd591 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
@@ -17,6 +17,7 @@
package androidx.appsearch.app;
import androidx.annotation.NonNull;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -64,6 +65,7 @@
}
/** Adds one or more document IDs to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIds(@NonNull String... ids) {
Preconditions.checkNotNull(ids);
@@ -72,6 +74,7 @@
}
/** Adds a collection of IDs to the request. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIds(@NonNull Collection<String> ids) {
Preconditions.checkNotNull(ids);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
index 12422eb..67550eb 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportSystemUsageRequest.java
@@ -17,6 +17,7 @@
package androidx.appsearch.app;
import androidx.annotation.NonNull;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.core.util.Preconditions;
/**
@@ -95,12 +96,12 @@
* Creates a {@link ReportSystemUsageRequest.Builder} instance.
*
* @param packageName The package name of the app which owns the document that was used
- * (e.g. from {@link SearchResult#getPackageName}).
- * @param databaseName The database in which the document that was used resides (e.g. from
- * {@link SearchResult#getDatabaseName}).
- * @param namespace The namespace of the document that was used (e.g. from
+ * (such as from {@link SearchResult#getPackageName}).
+ * @param databaseName The database in which the document that was used resides (such as
+ * from {@link SearchResult#getDatabaseName}).
+ * @param namespace The namespace of the document that was used (such as from
* {@link GenericDocument#getNamespace}.
- * @param documentId The ID of document that was used (e.g. from
+ * @param documentId The ID of document that was used (such as from
* {@link GenericDocument#getId}.
*/
public Builder(
@@ -123,6 +124,7 @@
* <p>If unset, this defaults to the current timestamp at the time that the
* {@link ReportSystemUsageRequest} is constructed.
*/
+ @CanIgnoreReturnValue
@NonNull
public ReportSystemUsageRequest.Builder setUsageTimestampMillis(
/*@exportToFramework:CurrentTimeMillisLong*/ long usageTimestampMillis) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
index 14b70c7..58e7d9b 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
@@ -17,6 +17,7 @@
package androidx.appsearch.app;
import androidx.annotation.NonNull;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.core.util.Preconditions;
/**
@@ -70,9 +71,9 @@
/**
* Creates a new {@link ReportUsageRequest.Builder} instance.
*
- * @param namespace The namespace of the document that was used (e.g. from
+ * @param namespace The namespace of the document that was used (such as from
* {@link GenericDocument#getNamespace}.
- * @param documentId The ID of document that was used (e.g. from
+ * @param documentId The ID of document that was used (such as from
* {@link GenericDocument#getId}.
*/
public Builder(@NonNull String namespace, @NonNull String documentId) {
@@ -89,6 +90,7 @@
* <p>If unset, this defaults to the current timestamp at the time that the
* {@link ReportUsageRequest} is constructed.
*/
+ @CanIgnoreReturnValue
@NonNull
public ReportUsageRequest.Builder setUsageTimestampMillis(
/*@exportToFramework:CurrentTimeMillisLong*/ long usageTimestampMillis) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
index 32e4507..9768a3a 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
@@ -22,6 +22,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.core.util.ObjectsCompat;
import androidx.core.util.Preconditions;
@@ -244,6 +245,7 @@
* @throws AppSearchException if an error occurs converting a document class into a
* {@link GenericDocument}.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setDocument(@NonNull Object document) throws AppSearchException {
Preconditions.checkNotNull(document);
@@ -253,6 +255,7 @@
// @exportToFramework:endStrip()
/** Sets the document which matched. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setGenericDocument(@NonNull GenericDocument document) {
Preconditions.checkNotNull(document);
@@ -262,6 +265,7 @@
}
/** Adds another match to this SearchResult. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addMatchInfo(@NonNull MatchInfo matchInfo) {
Preconditions.checkState(
@@ -274,6 +278,7 @@
}
/** Sets the ranking signal of the matched document in this SearchResult. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRankingSignal(double rankingSignal) {
resetIfBuilt();
@@ -285,6 +290,7 @@
* Adds a {@link SearchResult} that was joined by the {@link JoinSpec}.
* @param joinedResult The joined SearchResult to add.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addJoinedResult(@NonNull SearchResult joinedResult) {
resetIfBuilt();
@@ -316,8 +322,8 @@
}
/**
- * This class represents match objects for any Snippets that might be present in
- * {@link SearchResults} from a query. Using this class, the user can get:
+ * This class represents match objects for any snippets that might be present in
+ * {@link SearchResults} from a query. Using this class, you can get:
* <ul>
* <li>the full text - all of the text in that String property</li>
* <li>the exact term match - the 'term' (full word) that matched the query</li>
@@ -635,7 +641,7 @@
* property in the document these snippets correspond to.
*
* <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
- * For class example 1 this returns "subject".
+ * For class example 1, this returns "subject".
*
* @param propertyPath A dot-delimited sequence of property names indicating which
* property in the document these snippets correspond to.
@@ -645,6 +651,7 @@
}
/** Sets the exact {@link MatchRange} corresponding to the given entry. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
mExactMatchRange = Preconditions.checkNotNull(matchRange);
@@ -653,6 +660,7 @@
/** Sets the submatch {@link MatchRange} corresponding to the given entry. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSubmatchRange(@NonNull MatchRange matchRange) {
mSubmatchRange = Preconditions.checkNotNull(matchRange);
@@ -660,6 +668,7 @@
}
/** Sets the snippet {@link MatchRange} corresponding to the given entry. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSnippetRange(@NonNull MatchRange matchRange) {
mSnippetRange = Preconditions.checkNotNull(matchRange);
@@ -697,7 +706,7 @@
* <p> All ranges are finite, and the left side of the range is always {@code <=} the right
* side of the range.
*
- * <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
+ * <p> Example: MatchRange(0, 100) represents hundred ints from 0 to 99."
*/
public static final class MatchRange {
private final int mEnd;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
index 9f951fd..6189cb0 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
@@ -25,6 +25,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.annotation.Document;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.util.BundleUtil;
@@ -100,12 +101,14 @@
/**
* Query terms will only match exact tokens in the index.
- * <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football".
+ * <p>For example, a query term "foo" will only match indexed token "foo", and not "foot" or
+ * "football".
*/
public static final int TERM_MATCH_EXACT_ONLY = 1;
/**
* Query terms will match indexed tokens when the query term is a prefix of the token.
- * <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football".
+ * <p>For example, a query term "foo" will match indexed tokens like "foo", "foot", and
+ * "football".
*/
public static final int TERM_MATCH_PREFIX = 2;
@@ -188,23 +191,33 @@
*/
@IntDef(flag = true, value = {
GROUPING_TYPE_PER_PACKAGE,
- GROUPING_TYPE_PER_NAMESPACE
+ GROUPING_TYPE_PER_NAMESPACE,
+ GROUPING_TYPE_PER_SCHEMA
})
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Retention(RetentionPolicy.SOURCE)
public @interface GroupingType {
}
-
/**
* Results should be grouped together by package for the purpose of enforcing a limit on the
* number of results returned per package.
*/
- public static final int GROUPING_TYPE_PER_PACKAGE = 0b01;
+ public static final int GROUPING_TYPE_PER_PACKAGE = 1 << 0;
/**
* Results should be grouped together by namespace for the purpose of enforcing a limit on the
* number of results returned per namespace.
*/
- public static final int GROUPING_TYPE_PER_NAMESPACE = 0b10;
+ public static final int GROUPING_TYPE_PER_NAMESPACE = 1 << 1;
+ /**
+ * Results should be grouped together by schema type for the purpose of enforcing a limit on the
+ * number of results returned per schema type.
+ */
+ // @exportToFramework:startStrip()
+ @RequiresFeature(
+ enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+ name = Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA)
+ // @exportToFramework:endStrip()
+ public static final int GROUPING_TYPE_PER_SCHEMA = 1 << 2;
private final Bundle mBundle;
@@ -227,7 +240,8 @@
}
/** Returns how the query terms should match terms in the index. */
- public @TermMatch int getTermMatch() {
+ @TermMatch
+ public int getTermMatch() {
return mBundle.getInt(TERM_MATCH_TYPE_FIELD, -1);
}
@@ -281,12 +295,14 @@
}
/** Returns the ranking strategy. */
- public @RankingStrategy int getRankingStrategy() {
+ @RankingStrategy
+ public int getRankingStrategy() {
return mBundle.getInt(RANKING_STRATEGY_FIELD);
}
/** Returns the order of returned search results (descending or ascending). */
- public @Order int getOrder() {
+ @Order
+ public int getOrder() {
return mBundle.getInt(ORDER_FIELD);
}
@@ -415,7 +431,8 @@
* Get the type of grouping limit to apply, or 0 if {@link Builder#setResultGrouping} was not
* called.
*/
- public @GroupingType int getResultGroupingTypeFlags() {
+ @GroupingType
+ public int getResultGroupingTypeFlags() {
return mBundle.getInt(RESULT_GROUPING_TYPE_FLAGS);
}
@@ -454,21 +471,21 @@
* Returns whether the {@link Features#NUMERIC_SEARCH} feature is enabled.
*/
public boolean isNumericSearchEnabled() {
- return getEnabledFeatures().contains(Features.NUMERIC_SEARCH);
+ return getEnabledFeatures().contains(FeatureConstants.NUMERIC_SEARCH);
}
/**
* Returns whether the {@link Features#VERBATIM_SEARCH} feature is enabled.
*/
public boolean isVerbatimSearchEnabled() {
- return getEnabledFeatures().contains(Features.VERBATIM_SEARCH);
+ return getEnabledFeatures().contains(FeatureConstants.VERBATIM_SEARCH);
}
/**
* Returns whether the {@link Features#LIST_FILTER_QUERY_LANGUAGE} feature is enabled.
*/
public boolean isListFilterQueryLanguageEnabled() {
- return getEnabledFeatures().contains(Features.LIST_FILTER_QUERY_LANGUAGE);
+ return getEnabledFeatures().contains(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE);
}
/**
@@ -493,24 +510,25 @@
private Bundle mTypePropertyWeights = new Bundle();
private int mResultCountPerPage = DEFAULT_NUM_PER_PAGE;
- private @TermMatch int mTermMatchType = TERM_MATCH_PREFIX;
+ @TermMatch private int mTermMatchType = TERM_MATCH_PREFIX;
private int mSnippetCount = 0;
private int mSnippetCountPerProperty = MAX_SNIPPET_PER_PROPERTY_COUNT;
private int mMaxSnippetSize = 0;
- private @RankingStrategy int mRankingStrategy = RANKING_STRATEGY_NONE;
- private @Order int mOrder = ORDER_DESCENDING;
- private @GroupingType int mGroupingTypeFlags = 0;
+ @RankingStrategy private int mRankingStrategy = RANKING_STRATEGY_NONE;
+ @Order private int mOrder = ORDER_DESCENDING;
+ @GroupingType private int mGroupingTypeFlags = 0;
private int mGroupingLimit = 0;
private JoinSpec mJoinSpec;
private String mAdvancedRankingExpression = "";
private boolean mBuilt = false;
/**
- * Indicates how the query terms should match {@code TermMatchCode} in the index.
+ * Sets how the query terms should match {@code TermMatchCode} in the index.
*
* <p>If this method is not called, the default term match type is
* {@link SearchSpec#TERM_MATCH_PREFIX}.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setTermMatch(@TermMatch int termMatchType) {
Preconditions.checkArgumentInRange(termMatchType, TERM_MATCH_EXACT_ONLY,
@@ -526,6 +544,7 @@
*
* <p>If unset, the query will search over all schema types.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull String... schemas) {
Preconditions.checkNotNull(schemas);
@@ -539,6 +558,7 @@
*
* <p>If unset, the query will search over all schema types.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
Preconditions.checkNotNull(schemas);
@@ -555,9 +575,11 @@
*
* <p>If unset, the query will search over all schema types.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterDocumentClasses(
@@ -583,9 +605,11 @@
*
* <p>If unset, the query will search over all schema types.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas()
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
@@ -601,6 +625,7 @@
* have the specified namespaces.
* <p>If unset, the query will search over all namespaces.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterNamespaces(@NonNull String... namespaces) {
Preconditions.checkNotNull(namespaces);
@@ -613,6 +638,7 @@
* have the specified namespaces.
* <p>If unset, the query will search over all namespaces.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
Preconditions.checkNotNull(namespaces);
@@ -629,6 +655,7 @@
* If package names are specified which caller doesn't have access to, then those package
* names will be ignored.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterPackageNames(@NonNull String... packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -644,6 +671,7 @@
* If package names are specified which caller doesn't have access to, then those package
* names will be ignored.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
Preconditions.checkNotNull(packageNames);
@@ -657,6 +685,7 @@
*
* <p>The default number of results per page is 10.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder setResultCountPerPage(
@IntRange(from = 0, to = MAX_NUM_PER_PAGE) int resultCountPerPage) {
@@ -668,6 +697,7 @@
}
/** Sets ranking strategy for AppSearch results. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) {
Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
@@ -690,52 +720,41 @@
* <p>Numeric literals, arithmetic operators, mathematical functions, and document-based
* functions are supported to build expressions.
*
- * <p>The following are examples of numeric literals:
- * <ul>
- * <li>Integer
- * <p>Example: 0, 1, 2, 13
- * <li>Floating-point number
- * <p>Example: 0.333, 0.5, 123.456
- * <li>Negative number
- * <p>Example: -5, -10.5, -100.123
- * </ul>
- *
* <p>The following are supported arithmetic operators:
* <ul>
* <li>Addition(+)
- * <p>Example: "1 + 1" will be evaluated to 2.
* <li>Subtraction(-)
- * <p>Example: "2 - 1.5" will be evaluated to 0.5.
* <li>Multiplication(*)
- * <p>Example: "2 * -2" will be evaluated to -4.
- * <li>Division(/)
- * <p>Example: "5 / 2" will be evaluated to 2.5.
+ * <li>Floating Point Division(/)
* </ul>
*
- * <p>Multiplication and division have higher precedences than addition and subtraction,
- * but multiplication has the same precedence as division, and addition has the same
- * precedence as subtraction. Parentheses are supported to change precedences.
+ * <p>Operator precedences are compliant with the Java Language, and parentheses are
+ * supported. For example, "2.2 + (3 - 4) / 2" evaluates to 1.7.
*
- * <p>For example:
- * <ul>
- * <li>"2 + 3 - 4 * 5" will be evaluated to -15
- * <li>"(2 + 3) - (4 * 5)" will be evaluated to -15
- * <li>"2 + (3 - 4) * 5" will be evaluated to -3
- * </ul>
- *
- * <p>The following are supported mathematical functions:
+ * <p>The following are supported basic mathematical functions:
* <ul>
* <li>log(x) - the natural log of x
* <li>log(x, y) - the log of y with base x
* <li>pow(x, y) - x to the power of y
- * <li>max(v1, v2, ..., vn) with n > 0 - the maximum value among v1, ..., vn
- * <li>min(v1, v2, ..., vn) with n > 0 - the minimum value among v1, ..., vn
- * <li>sqrt(x) - the square root of x
- * <li>abs(x) - the absolute value of x
- * <li>sin(x), cos(x), tan(x) - trigonometric functions of x
+ * <li>sqrt(x)
+ * <li>abs(x)
+ * <li>sin(x), cos(x), tan(x)
* <li>Example: "max(abs(-100), 10) + pow(2, 10)" will be evaluated to 1124
* </ul>
*
+ * <p>The following variadic mathematical functions are supported, with n > 0. They also
+ * accept list value parameters. For example, if V is a value of list type, we can call
+ * sum(V) to get the sum of all the values in V. List literals are not supported, so a
+ * value of list type can only be constructed as a return value of some particular
+ * document-based functions.
+ * <ul>
+ * <li>max(v1, v2, ..., vn) or max(V)
+ * <li>min(v1, v2, ..., vn) or min(V)
+ * <li>len(v1, v2, ..., vn) or len(V)
+ * <li>sum(v1, v2, ..., vn) or sum(V)
+ * <li>avg(v1, v2, ..., vn) or avg(V)
+ * </ul>
+ *
* <p>Document-based functions must be called via "this", which represents the current
* document being scored. The following are supported document-based functions:
* <ul>
@@ -754,27 +773,61 @@
* document, where type must be evaluated to an integer from 1 to 2. Type 1 refers to
* usages reported by {@link AppSearchSession#reportUsageAsync}, and type 2 refers to
* usages reported by {@link GlobalSearchSession#reportSystemUsageAsync}.
+ * <li>this.childrenRankingSignals()
+ * <p>Returns a list of children ranking signals calculated by scoring the joined
+ * documents using the ranking strategy specified in the nested {@link SearchSpec}.
+ * Currently, a document can only be a child of another document in the context of
+ * joins. If this function is called without the Join API enabled, a type error will
+ * be raised.
+ * <li>this.propertyWeights()
+ * <p>Returns a list of the normalized weights of the matched properties for the
+ * current document being scored. Property weights come from what's specified in
+ * {@link SearchSpec}. After normalizing, each provided weight will be divided by the
+ * maximum weight, so that each of them will be <= 1.
* </ul>
*
* <p>Some errors may occur when using advanced ranking.
+ *
+ * <p>Syntax Error: the expression violates the syntax of the advanced ranking language.
+ * Below are some examples.
* <ul>
- * <li>Syntax Error: the expression violates the syntax of the advanced ranking
- * language, such as unbalanced parenthesis.
- * <li>Type Error: the expression fails a static type check, such as getting the wrong
- * number of arguments for a function.
- * <li>Evaluation Error: an error occurred while evaluating the value of the
- * expression, such as getting a non-finite value in the middle of evaluation.
- * Expressions like "1 / 0" and "log(0) fall into this category.
+ * <li>"1 + " - missing operand
+ * <li>"2 * (1 + 2))" - unbalanced parenthesis
+ * <li>"2 ^ 3" - unknown operator
+ * </ul>
+ *
+ * <p>Type Error: the expression fails a static type check. Below are some examples.
+ * <ul>
+ * <li>"sin(2, 3)" - wrong number of arguments for the sin function
+ * <li>"this.childrenRankingSignals() + 1" - cannot add a list with a number
+ * <li>"this.propertyWeights()" - the final type of the overall expression cannot be
+ * a list, which can be fixed by "max(this.propertyWeights())"
+ * <li>"abs(this.propertyWeights())" - the abs function does not support list type
+ * arguments
+ * <li>"print(2)" - unknown function
+ * </ul>
+ *
+ * <p>Evaluation Error: an error occurred while evaluating the value of the expression.
+ * Below are some examples.
+ * <ul>
+ * <li>"1 / 0", "log(0)", "1 + sqrt(-1)" - getting a non-finite value in the middle
+ * of evaluation
+ * <li>"this.usageCount(1 + 0.5)" - expect the argument to be an integer. Note that
+ * this is not a type error and "this.usageCount(1.5 + 1/2)" can succeed without any
+ * issues
+ * <li>"this.documentScore()" - in case of an IO error, this will be an evaluation error
* </ul>
*
* <p>Syntax errors and type errors will fail the entire search and will cause
- * {@link SearchResults#getNextPageAsync()} to throw an {@link AppSearchException}.
+ * {@link SearchResults#getNextPageAsync} to throw an {@link AppSearchException} with the
+ * result code of {@link AppSearchResult#RESULT_INVALID_ARGUMENT}.
* <p>Evaluation errors will result in the offending documents receiving the default score.
* For {@link #ORDER_DESCENDING}, the default score will be 0, for
* {@link #ORDER_ASCENDING} the default score will be infinity.
*
* @param advancedRankingExpression a non-empty string representing the ranking expression.
*/
+ @CanIgnoreReturnValue
@NonNull
// @exportToFramework:startStrip()
@RequiresFeature(
@@ -790,11 +843,12 @@
}
/**
- * Indicates the order of returned search results, the default is
+ * Sets the order of returned search results, the default is
* {@link #ORDER_DESCENDING}, meaning that results with higher scores come first.
*
* <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setOrder(@Order int order) {
Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
@@ -805,8 +859,8 @@
}
/**
- * Only the first {@code snippetCount} documents based on the ranking strategy
- * will have snippet information provided.
+ * Sets the {@code snippetCount} such that the first {@code snippetCount} documents based
+ * on the ranking strategy will have snippet information provided.
*
* <p>The list returned from {@link SearchResult#getMatchInfos} will contain at most this
* many entries.
@@ -814,6 +868,7 @@
* <p>If set to 0 (default), snippeting is disabled and the list returned from
* {@link SearchResult#getMatchInfos} will be empty.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder setSnippetCount(
@IntRange(from = 0, to = MAX_SNIPPET_COUNT) int snippetCount) {
@@ -834,6 +889,7 @@
* <p>The default behavior is to snippet all matches a property contains, up to the maximum
* value of 10,000.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder setSnippetCountPerProperty(
@IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT)
@@ -851,12 +907,13 @@
* {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects
* token boundaries, therefore the returned window may be smaller than requested.
*
- * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will
+ * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty String will
* be returned. If matches enabled is also set to false, then snippeting is disabled.
*
- * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will
- * return a window of "bar baz bat" which is only 11 bytes long.
+ * <p>For example, {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz"
+ * will return a window of "bar baz bat" which is only 11 bytes long.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder setMaxSnippetSize(
@IntRange(from = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize) {
@@ -878,6 +935,7 @@
* @param schema a string corresponding to the schema to add projections to.
* @param propertyPaths the projections to add.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder addProjection(
@NonNull String schema, @NonNull Collection<String> propertyPaths) {
@@ -956,6 +1014,7 @@
* @param schema a string corresponding to the schema to add projections to.
* @param propertyPaths the projections to add.
*/
+ @CanIgnoreReturnValue
@NonNull
public SearchSpec.Builder addProjectionPaths(
@NonNull String schema, @NonNull Collection<PropertyPath> propertyPaths) {
@@ -981,6 +1040,7 @@
* add projections to.
* @param propertyPaths the projections to add.
*/
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder") // Projections available from getProjections
@NonNull
public SearchSpec.Builder addProjectionsForDocumentClass(
@@ -1001,6 +1061,7 @@
* add projections to.
* @param propertyPaths the projections to add.
*/
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder") // Projections available from getProjections
@NonNull
public SearchSpec.Builder addProjectionPathsForDocumentClass(
@@ -1020,13 +1081,13 @@
* Sets the maximum number of results to return for each group, where groups are defined
* by grouping type.
*
- * <p>Calling this method will override any previous calls. So calling
- * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 7) and then calling
- * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 2) will result in only the latter, a
- * limit of two results per package, being applied. Or calling setResultGrouping
- * (GROUPING_TYPE_PER_PACKAGE, 1) and then calling setResultGrouping
- * (GROUPING_TYPE_PER_PACKAGE | GROUPING_PER_NAMESPACE, 5) will result in five results
- * per package per namespace.
+ * <p>Calling this method will override any previous calls. So calling {@code
+ * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 7)} and then calling {@code
+ * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 2)} will result in only the latter, a limit
+ * of two results per package, being applied. Or calling {@code setResultGrouping
+ * (GROUPING_TYPE_PER_PACKAGE, 1)} and then calling {@code setResultGrouping
+ * (GROUPING_TYPE_PER_PACKAGE | GROUPING_PER_NAMESPACE, 5)} will result in five results per
+ * package per namespace.
*
* @param groupingTypeFlags One or more combination of grouping types.
* @param limit Number of results to return per {@code groupingTypeFlags}.
@@ -1034,6 +1095,7 @@
*/
// Individual parameters available from getResultGroupingTypeFlags and
// getResultGroupingLimit
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setResultGrouping(@GroupingType int groupingTypeFlags, int limit) {
@@ -1076,6 +1138,7 @@
* @throws IllegalArgumentException if a weight is equal to or less than 0.0.
*/
// @exportToFramework:startStrip()
+ @CanIgnoreReturnValue
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
@@ -1110,6 +1173,7 @@
* @param joinSpec a specification on how to perform the Join operation.
*/
// @exportToFramework:startStrip()
+ @CanIgnoreReturnValue
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
name = Features.JOIN_SPEC_AND_QUALIFIED_ID)
@@ -1152,6 +1216,7 @@
* @throws IllegalArgumentException if a weight is equal to or less than 0.0.
*/
// @exportToFramework:startStrip()
+ @CanIgnoreReturnValue
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
@@ -1206,6 +1271,7 @@
* classpath
* @throws IllegalArgumentException if a weight is equal to or less than 0.0.
*/
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
@@ -1253,6 +1319,7 @@
* classpath
* @throws IllegalArgumentException if a weight is equal to or less than 0.0.
*/
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
@@ -1285,7 +1352,7 @@
// @exportToFramework:endStrip()
@NonNull
public Builder setNumericSearchEnabled(boolean enabled) {
- modifyEnabledFeature(Features.NUMERIC_SEARCH, enabled);
+ modifyEnabledFeature(FeatureConstants.NUMERIC_SEARCH, enabled);
return this;
}
@@ -1300,8 +1367,8 @@
* verbatim search features within the query language that allows clients to search
* using the verbatim string operator.
*
- * <p>Ex. The verbatim string operator '"foo/bar" OR baz' will ensure that 'foo/bar' is
- * treated as a single 'verbatim' token.
+ * <p>For example, The verbatim string operator '"foo/bar" OR baz' will ensure that
+ * 'foo/bar' is treated as a single 'verbatim' token.
*/
// @exportToFramework:startStrip()
@RequiresFeature(
@@ -1310,7 +1377,7 @@
// @exportToFramework:endStrip()
@NonNull
public Builder setVerbatimSearchEnabled(boolean enabled) {
- modifyEnabledFeature(Features.VERBATIM_SEARCH, enabled);
+ modifyEnabledFeature(FeatureConstants.VERBATIM_SEARCH, enabled);
return this;
}
@@ -1350,7 +1417,7 @@
// @exportToFramework:endStrip()
@NonNull
public Builder setListFilterQueryLanguageEnabled(boolean enabled) {
- modifyEnabledFeature(Features.LIST_FILTER_QUERY_LANGUAGE, enabled);
+ modifyEnabledFeature(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE, enabled);
return this;
}
@@ -1399,9 +1466,11 @@
bundle.putInt(RESULT_GROUPING_TYPE_FLAGS, mGroupingTypeFlags);
bundle.putInt(RESULT_GROUPING_LIMIT, mGroupingLimit);
if (!mTypePropertyWeights.isEmpty()
- && RANKING_STRATEGY_RELEVANCE_SCORE != mRankingStrategy) {
+ && RANKING_STRATEGY_RELEVANCE_SCORE != mRankingStrategy
+ && RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION != mRankingStrategy) {
throw new IllegalArgumentException("Property weights are only compatible with the "
- + "RANKING_STRATEGY_RELEVANCE_SCORE ranking strategy.");
+ + "RANKING_STRATEGY_RELEVANCE_SCORE and "
+ + "RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION ranking strategies.");
}
bundle.putBundle(TYPE_PROPERTY_WEIGHTS_FIELD, mTypePropertyWeights);
bundle.putString(ADVANCED_RANKING_EXPRESSION, mAdvancedRankingExpression);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
index 2fea497..0007ece 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
@@ -21,13 +21,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.util.BundleUtil;
import androidx.core.util.Preconditions;
/**
* The result class of the {@link AppSearchSession#searchSuggestionAsync}.
*/
-public class SearchSuggestionResult {
+public final class SearchSuggestionResult {
private static final String SUGGESTED_RESULT_FIELD = "suggestedResult";
private final Bundle mBundle;
@@ -92,6 +93,7 @@
*
* <p>The suggested result should only contain lowercase or special characters.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setSuggestedResult(@NonNull String suggestedResult) {
Preconditions.checkNotNull(suggestedResult);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
index b195591..26192fa 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
@@ -15,7 +15,6 @@
*/
package androidx.appsearch.app;
-import static androidx.appsearch.app.AppSearchResult.RESULT_INVALID_ARGUMENT;
import android.annotation.SuppressLint;
import android.os.Bundle;
@@ -24,6 +23,7 @@
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.annotation.Document;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.appsearch.util.BundleUtil;
@@ -45,9 +45,9 @@
* This class represents the specification logic for AppSearch. It can be used to set the filter
* and settings of search a suggestions.
*
- * @see AppSearchSession#searchSuggestionAsync(String, SearchSuggestionSpec)
+ * @see AppSearchSession#searchSuggestionAsync
*/
-public class SearchSuggestionSpec {
+public final class SearchSuggestionSpec {
static final String NAMESPACE_FIELD = "namespace";
static final String SCHEMA_FIELD = "schema";
static final String PROPERTY_FIELD = "property";
@@ -143,7 +143,8 @@
}
/** Returns the ranking strategy. */
- public @SuggestionRankingStrategy int getRankingStrategy() {
+ @SuggestionRankingStrategy
+ public int getRankingStrategy() {
return mBundle.getInt(RANKING_STRATEGY_FIELD);
}
@@ -222,7 +223,7 @@
private Bundle mTypePropertyFilters = new Bundle();
private Bundle mDocumentIds = new Bundle();
private final int mTotalResultCount;
- private @SuggestionRankingStrategy int mRankingStrategy =
+ @SuggestionRankingStrategy private int mRankingStrategy =
SUGGESTION_RANKING_STRATEGY_DOCUMENT_COUNT;
private boolean mBuilt = false;
@@ -243,6 +244,7 @@
*
* <p>If unset, the query will search over all namespaces.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterNamespaces(@NonNull String... namespaces) {
Preconditions.checkNotNull(namespaces);
@@ -256,6 +258,7 @@
*
* <p>If unset, the query will search over all namespaces.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
Preconditions.checkNotNull(namespaces);
@@ -270,6 +273,7 @@
* <p>The default value {@link #SUGGESTION_RANKING_STRATEGY_DOCUMENT_COUNT} will be used if
* this method is never called.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setRankingStrategy(@SuggestionRankingStrategy int rankingStrategy) {
Preconditions.checkArgumentInRange(rankingStrategy,
@@ -286,6 +290,7 @@
*
* <p>If unset, the query will search over all schema.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull String... schemaTypes) {
Preconditions.checkNotNull(schemaTypes);
@@ -299,6 +304,7 @@
*
* <p>If unset, the query will search over all schema.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull Collection<String> schemaTypes) {
Preconditions.checkNotNull(schemaTypes);
@@ -315,10 +321,12 @@
*
* <p>If unset, the query will search over all schema.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas()
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
throws AppSearchException {
@@ -337,10 +345,12 @@
*
* <p>If unset, the query will search over all schema.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentClasses(
@NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException {
@@ -496,6 +506,7 @@
*
* <p>If unset, the query will search over all documents.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentIds(@NonNull String namespace,
@NonNull String... documentIds) {
@@ -511,6 +522,7 @@
*
* <p>If unset, the query will search over all documents.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentIds(@NonNull String namespace,
@NonNull Collection<String> documentIds) {
@@ -527,13 +539,13 @@
/** Constructs a new {@link SearchSpec} from the contents of this builder. */
@NonNull
- public SearchSuggestionSpec build() throws AppSearchException {
+ public SearchSuggestionSpec build() {
Bundle bundle = new Bundle();
if (!mSchemas.isEmpty()) {
Set<String> schemaFilter = new ArraySet<>(mSchemas);
for (String schema : mTypePropertyFilters.keySet()) {
if (!schemaFilter.contains(schema)) {
- throw new AppSearchException(RESULT_INVALID_ARGUMENT,
+ throw new IllegalStateException(
"The schema: " + schema + " exists in the property filter but "
+ "doesn't exist in the schema filter.");
}
@@ -543,7 +555,7 @@
Set<String> namespaceFilter = new ArraySet<>(mNamespaces);
for (String namespace : mDocumentIds.keySet()) {
if (!namespaceFilter.contains(namespace)) {
- throw new AppSearchException(RESULT_INVALID_ARGUMENT,
+ throw new IllegalStateException(
"The namespace: " + namespace + " exists in the document id "
+ "filter but doesn't exist in the namespace filter.");
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
index 52e953a..9b950ed 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
@@ -23,6 +23,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresFeature;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.exceptions.AppSearchException;
import androidx.collection.ArrayMap;
import androidx.collection.ArraySet;
@@ -76,7 +77,7 @@
* This deletes all documents that are incompatible with the new schema. The new schema is
* then saved and persisted to disk.
* <li>Add a {@link Migrator} for each incompatible type and make no deletion. The migrator
- * will migrate documents from it's old schema version to the new version. Migrated types
+ * will migrate documents from its old schema version to the new version. Migrated types
* will be set into both {@link SetSchemaResponse#getIncompatibleTypes()} and
* {@link SetSchemaResponse#getMigratedTypes()}. See the migration section below.
* </ul>
@@ -318,6 +319,7 @@
*
* <p>Any documents of these types will be displayed on system UI surfaces by default.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
Preconditions.checkNotNull(schemas);
@@ -330,6 +332,7 @@
*
* <p>An {@link AppSearchSchema} object represents one type of structured data.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
Preconditions.checkNotNull(schemas);
@@ -343,12 +346,15 @@
* Adds one or more {@link androidx.appsearch.annotation.Document} annotated classes to the
* schema.
*
+ * <p>Merged list available from {@link #getSchemas()}.
+ *
* @param documentClasses classes annotated with
* {@link androidx.appsearch.annotation.Document}.
* @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
* has not generated a schema for the given document classes.
*/
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getSchemas()
+ @CanIgnoreReturnValue
+ @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addDocumentClasses(@NonNull Class<?>... documentClasses)
throws AppSearchException {
@@ -361,12 +367,18 @@
* Adds a collection of {@link androidx.appsearch.annotation.Document} annotated classes to
* the schema.
*
+ * <p>This will also add all {@link androidx.appsearch.annotation.Document} classes
+ * referenced by the schema via document properties.
+ *
+ * <p>Merged list available from {@link #getSchemas()}.
+ *
* @param documentClasses classes annotated with
* {@link androidx.appsearch.annotation.Document}.
* @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
* has not generated a schema for the given document classes.
*/
- @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getSchemas()
+ @CanIgnoreReturnValue
+ @SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder addDocumentClasses(@NonNull Collection<? extends Class<?>> documentClasses)
throws AppSearchException {
@@ -377,6 +389,7 @@
for (Class<?> documentClass : documentClasses) {
DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
schemas.add(factory.getSchema());
+ addDocumentClasses(factory.getNestedDocumentClasses());
}
return addSchemas(schemas);
}
@@ -397,6 +410,7 @@
* @param displayed Whether documents of this type will be displayed on system UI surfaces.
*/
// Merged list available from getSchemasNotDisplayedBySystem
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setSchemaTypeDisplayedBySystem(
@@ -438,6 +452,7 @@
* @throws IllegalArgumentException – if input unsupported permission.
*/
// Merged list available from getRequiredPermissionsForSchemaTypeVisibility
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
// @exportToFramework:startStrip()
@RequiresFeature(
@@ -465,6 +480,7 @@
/** Clears all required permissions combinations for the given schema type. */
// @exportToFramework:startStrip()
+ @CanIgnoreReturnValue
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
@@ -498,6 +514,7 @@
* @param packageIdentifier Represents the package that will be granted visibility.
*/
// Merged list available from getSchemasVisibleToPackages
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setSchemaTypeVisibilityForPackage(
@@ -553,6 +570,7 @@
* @see SetSchemaRequest.Builder#addSchemas
* @see AppSearchSession#setSchemaAsync
*/
+ @CanIgnoreReturnValue
@NonNull
@SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) {
@@ -580,7 +598,7 @@
* {@link Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this
* {@link SetSchemaRequest}.
*
- * @param migrators A {@link Map} of migrators that translate a document from it's current
+ * @param migrators A {@link Map} of migrators that translate a document from its current
* version to the final version set via {@link #setVersion}. The key of
* the map is the schema type that the {@link Migrator} value applies to.
*
@@ -588,6 +606,7 @@
* @see SetSchemaRequest.Builder#addSchemas
* @see AppSearchSession#setSchemaAsync
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setMigrators(@NonNull Map<String, Migrator> migrators) {
Preconditions.checkNotNull(migrators);
@@ -610,6 +629,8 @@
* <p>The default behavior, if this method is not called, is to allow types to be
* displayed on system UI surfaces.
*
+ * <p> Merged list available from {@link #getSchemasNotDisplayedBySystem()}.
+ *
* @param documentClass A class annotated with
* {@link androidx.appsearch.annotation.Document}, the visibility of
* which will be configured
@@ -618,7 +639,7 @@
* @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
* has not generated a schema for the given document class.
*/
- // Merged list available from getSchemasNotDisplayedBySystem
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setDocumentClassDisplayedBySystem(@NonNull Class<?> documentClass,
@@ -647,6 +668,8 @@
*
* <p>By default, app data sharing between applications is disabled.
*
+ * <p>Merged list available from {@link #getSchemasVisibleToPackages()}.
+ *
* @param documentClass The {@link androidx.appsearch.annotation.Document} class to set
* visibility on.
* @param visible Whether the {@code documentClass} will be visible or not.
@@ -654,7 +677,7 @@
* @throws AppSearchException if {@code androidx.appsearch.compiler.AppSearchCompiler}
* has not generated a schema for the given document class.
*/
- // Merged list available from getSchemasVisibleToPackages
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setDocumentClassVisibilityForPackage(@NonNull Class<?> documentClass,
@@ -682,6 +705,7 @@
* {@link #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE},
* {@link #READ_HOME_APP_SEARCH_DATA} and {@link #READ_ASSISTANT_APP_SEARCH_DATA}.
*
+ * <p>Merged map available from {@link #getRequiredPermissionsForSchemaTypeVisibility()}.
* @see android.Manifest.permission#READ_SMS
* @see android.Manifest.permission#READ_CALENDAR
* @see android.Manifest.permission#READ_CONTACTS
@@ -695,7 +719,7 @@
* schema.
* @throws IllegalArgumentException – if input unsupported permission.
*/
- // Merged map available from getRequiredPermissionsForSchemaTypeVisibility
+ @CanIgnoreReturnValue
@SuppressLint("MissingGetterMatchingBuilder")
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
@@ -714,6 +738,7 @@
}
/** Clears all required permissions combinations for the given schema type. */
+ @CanIgnoreReturnValue
@RequiresFeature(
enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
@@ -740,6 +765,7 @@
*
* <p>By default, this is {@code false}.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setForceOverride(boolean forceOverride) {
resetIfBuilt();
@@ -775,6 +801,7 @@
* @see Migrator
* @see SetSchemaRequest.Builder#setMigrator
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setVersion(@IntRange(from = 1) int version) {
Preconditions.checkArgument(version >= 1, "Version must be a positive number.");
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
index 3f8ce5e..7f26110 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -182,6 +183,7 @@
private boolean mBuilt = false;
/** Adds {@link MigrationFailure}s to the list of migration failures. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addMigrationFailures(
@NonNull Collection<MigrationFailure> migrationFailures) {
@@ -192,6 +194,7 @@
}
/** Adds a {@link MigrationFailure} to the list of migration failures. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) {
Preconditions.checkNotNull(migrationFailure);
@@ -200,7 +203,8 @@
return this;
}
- /** Adds deletedTypes to the list of deleted schema types. */
+ /** Adds {@code deletedTypes} to the list of deleted schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkNotNull(deletedTypes);
@@ -209,7 +213,8 @@
return this;
}
- /** Adds one deletedType to the list of deleted schema types. */
+ /** Adds one {@code deletedType} to the list of deleted schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addDeletedType(@NonNull String deletedType) {
Preconditions.checkNotNull(deletedType);
@@ -218,7 +223,8 @@
return this;
}
- /** Adds incompatibleTypes to the list of incompatible schema types. */
+ /** Adds {@code incompatibleTypes} to the list of incompatible schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkNotNull(incompatibleTypes);
@@ -227,7 +233,8 @@
return this;
}
- /** Adds one incompatibleType to the list of incompatible schema types. */
+ /** Adds one {@code incompatibleType} to the list of incompatible schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addIncompatibleType(@NonNull String incompatibleType) {
Preconditions.checkNotNull(incompatibleType);
@@ -236,7 +243,8 @@
return this;
}
- /** Adds migratedTypes to the list of migrated schema types. */
+ /** Adds {@code migratedTypes} to the list of migrated schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkNotNull(migratedTypes);
@@ -245,7 +253,8 @@
return this;
}
- /** Adds one migratedType to the list of migrated schema types. */
+ /** Adds one {@code migratedType} to the list of migrated schema types. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addMigratedType(@NonNull String migratedType) {
Preconditions.checkNotNull(migratedType);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
index 0d7901d..5778bf8 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
@@ -20,6 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.core.util.Preconditions;
/** The response class of {@code AppSearchSession#getStorageInfo}. */
@@ -78,6 +79,7 @@
private int mAliveNamespacesCount;
/** Sets the size in bytes. */
+ @CanIgnoreReturnValue
@NonNull
public StorageInfo.Builder setSizeBytes(long sizeBytes) {
mSizeBytes = sizeBytes;
@@ -85,6 +87,7 @@
}
/** Sets the number of alive documents. */
+ @CanIgnoreReturnValue
@NonNull
public StorageInfo.Builder setAliveDocumentsCount(int aliveDocumentsCount) {
mAliveDocumentsCount = aliveDocumentsCount;
@@ -92,6 +95,7 @@
}
/** Sets the number of alive namespaces. */
+ @CanIgnoreReturnValue
@NonNull
public StorageInfo.Builder setAliveNamespacesCount(int aliveNamespacesCount) {
mAliveNamespacesCount = aliveNamespacesCount;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
index fd79bd6..511b79c 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
@@ -20,6 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -36,7 +37,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class VisibilityDocument extends GenericDocument {
/**
- * The Schema type for documents that hold AppSearch's metadata, e.g. visibility settings.
+ * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
*/
public static final String SCHEMA_TYPE = "VisibilityType";
/** Namespace of documents that contain visibility settings */
@@ -162,6 +163,7 @@
}
/** Sets whether this schema has opted out of platform surfacing. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY,
@@ -169,6 +171,7 @@
}
/** Add {@link PackageIdentifier} of packages which has access to this schema. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
Preconditions.checkNotNull(packageIdentifiers);
@@ -177,6 +180,7 @@
}
/** Add {@link PackageIdentifier} of packages which has access to this schema. */
+ @CanIgnoreReturnValue
@NonNull
public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
Preconditions.checkNotNull(packageIdentifier);
@@ -185,12 +189,13 @@
}
/**
- * Set required permission sets for a package needs to hold to the schema this
+ * Sets required permission sets for a package needs to hold to the schema this
* {@link VisibilityDocument} represents.
*
* <p> The querier could have access if they holds ALL required permissions of ANY of the
* individual value sets.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setVisibleToPermissions(@NonNull Set<Set<Integer>> visibleToPermissions) {
Preconditions.checkNotNull(visibleToPermissions);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
index e859cb9..4c01317 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
@@ -19,6 +19,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.collection.ArraySet;
import java.util.Set;
@@ -32,7 +33,7 @@
public class VisibilityPermissionDocument extends GenericDocument {
/**
- * The Schema type for documents that hold AppSearch's metadata, e.g. visibility settings.
+ * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
*/
public static final String SCHEMA_TYPE = "VisibilityPermissionType";
@@ -76,6 +77,7 @@
}
/** Sets whether this schema has opted out of platform surfacing. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setVisibleToAllRequiredPermissions(
@NonNull Set<Integer> allRequiredPermissions) {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java b/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
index 98689f5..2930d2d 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
@@ -73,7 +73,8 @@
*
* @return One of the constants documented in {@link AppSearchResult#getResultCode}.
*/
- public @AppSearchResult.ResultCode int getResultCode() {
+ @AppSearchResult.ResultCode
+ public int getResultCode() {
return mResultCode;
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/AppSearchObserverCallback.java b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/AppSearchObserverCallback.java
deleted file mode 100644
index c01917e..0000000
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/AppSearchObserverCallback.java
+++ /dev/null
@@ -1,28 +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.appsearch.observer;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * @deprecated use {@link ObserverCallback} instead.
- * @hide
- */
-// TODO(b/209734214): Remove this after dogfooders and devices have migrated away from this class.
-@Deprecated
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface AppSearchObserverCallback extends ObserverCallback {}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/DocumentChangeInfo.java b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/DocumentChangeInfo.java
index 1fc52a0..8aede0f 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/DocumentChangeInfo.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/DocumentChangeInfo.java
@@ -27,8 +27,8 @@
/**
* Contains information about an individual change detected by an {@link ObserverCallback}.
*
- * <p>This class reports information about document changes, i.e. when documents were added, updated
- * or removed.
+ * <p>This class reports information about document changes, that is, when documents were added,
+ * updated or removed.
*
* <p>Changes are grouped by package, database, schema type and namespace. Each unique
* combination of these items will generate a unique {@link DocumentChangeInfo}.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
index 6e3705e..a46b4ed 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.annotation.Document;
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.DocumentClassFactoryRegistry;
@@ -85,7 +86,7 @@
return mFilterSchemas;
}
- /** Builder for ObserverSpec instances. */
+ /** Builder for {@link ObserverSpec} instances. */
public static final class Builder {
private ArrayList<String> mFilterSchemas = new ArrayList<>();
private boolean mBuilt = false;
@@ -96,6 +97,7 @@
*
* <p>If unset, the observer will match documents of all types.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull String... schemas) {
Preconditions.checkNotNull(schemas);
@@ -109,6 +111,7 @@
*
* <p>If unset, the observer will match documents of all types.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
Preconditions.checkNotNull(schemas);
@@ -124,10 +127,12 @@
*
* <p>If unset, the observer will match documents of all types.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas()
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
throws AppSearchException {
@@ -142,10 +147,12 @@
*
* <p>If unset, the observer will match documents of all types.
*
+ * <p>Merged list available from {@link #getFilterSchemas()}.
+ *
* @param documentClasses classes annotated with {@link Document}.
*/
- // Merged list available from getFilterSchemas
@SuppressLint("MissingGetterMatchingBuilder")
+ @CanIgnoreReturnValue
@NonNull
public Builder addFilterDocumentClasses(
@NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
index 3cff1133..48d5b23 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
@@ -21,6 +21,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.appsearch.app.SetSchemaRequest;
import androidx.appsearch.util.BundleUtil;
@@ -220,6 +221,7 @@
}
/** Sets status code for the schema migration action. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
mBundle.putInt(STATUS_CODE_FIELD, statusCode);
@@ -227,6 +229,7 @@
}
/** Sets the latency for waiting the executor. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setExecutorAcquisitionLatencyMillis(int executorAcquisitionLatencyMillis) {
mBundle.putInt(EXECUTOR_ACQUISITION_MILLIS_FIELD, executorAcquisitionLatencyMillis);
@@ -235,6 +238,7 @@
/** Sets total latency for the schema migration action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mBundle.putInt(TOTAL_LATENCY_MILLIS_FIELD, totalLatencyMillis);
@@ -242,6 +246,7 @@
}
/** Sets latency for the GetSchema action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setGetSchemaLatencyMillis(int getSchemaLatencyMillis) {
mBundle.putInt(GET_SCHEMA_LATENCY_MILLIS_FIELD, getSchemaLatencyMillis);
@@ -252,6 +257,7 @@
* Sets latency for querying all documents that need to be migrated to new version and
* transforming documents to new version in milliseconds.
*/
+ @CanIgnoreReturnValue
@NonNull
public Builder setQueryAndTransformLatencyMillis(
int queryAndTransformLatencyMillis) {
@@ -261,6 +267,7 @@
}
/** Sets latency of first SetSchema action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setFirstSetSchemaLatencyMillis(
int firstSetSchemaLatencyMillis) {
@@ -269,6 +276,7 @@
}
/** Returns status of the first SetSchema action. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setIsFirstSetSchemaSuccess(boolean isFirstSetSchemaSuccess) {
mBundle.putBoolean(IS_FIRST_SET_SCHEMA_SUCCESS_FIELD, isFirstSetSchemaSuccess);
@@ -276,6 +284,7 @@
}
/** Sets latency of second SetSchema action in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSecondSetSchemaLatencyMillis(
int secondSetSchemaLatencyMillis) {
@@ -284,6 +293,7 @@
}
/** Sets latency for putting migrated document to Icing lib in milliseconds. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setSaveDocumentLatencyMillis(
int saveDocumentLatencyMillis) {
@@ -292,6 +302,7 @@
}
/** Sets number of document that need to be migrated to another version. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalNeedMigratedDocumentCount(int migratedDocumentCount) {
mBundle.putInt(TOTAL_NEED_MIGRATED_DOCUMENT_COUNT_FIELD, migratedDocumentCount);
@@ -299,6 +310,7 @@
}
/** Sets total document count of successfully migrated and saved in Icing. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setTotalSuccessMigratedDocumentCount(
int totalSuccessMigratedDocumentCount) {
@@ -308,6 +320,7 @@
}
/** Sets number of {@link androidx.appsearch.app.SetSchemaResponse.MigrationFailure}. */
+ @CanIgnoreReturnValue
@NonNull
public Builder setMigrationFailureCount(int migrationFailureCount) {
mBundle.putInt(MIGRATION_FAILURE_COUNT_FIELD, migrationFailureCount);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/util/IndentingStringBuilder.java b/appsearch/appsearch/src/main/java/androidx/appsearch/util/IndentingStringBuilder.java
index ea5717e..20ef8fa 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/util/IndentingStringBuilder.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/util/IndentingStringBuilder.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
/**
* Utility for building indented strings.
@@ -41,6 +42,7 @@
/**
* Increases the indent level by one for appended strings.
*/
+ @CanIgnoreReturnValue
@NonNull
public IndentingStringBuilder increaseIndentLevel() {
mIndentLevel++;
@@ -50,6 +52,7 @@
/**
* Decreases the indent level by one for appended strings.
*/
+ @CanIgnoreReturnValue
@NonNull
public IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException {
if (mIndentLevel == 0) {
@@ -64,6 +67,7 @@
*
* <p>Indentation is applied after each newline character.
*/
+ @CanIgnoreReturnValue
@NonNull
public IndentingStringBuilder append(@NonNull String str) {
applyIndentToString(str);
@@ -76,6 +80,7 @@
*
* <p>Indentation is applied after each newline character.
*/
+ @CanIgnoreReturnValue
@NonNull
public IndentingStringBuilder append(@NonNull Object obj) {
applyIndentToString(obj.toString());
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
index a80f718..5adef09 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
@@ -22,11 +22,15 @@
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.WildcardTypeName;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
@@ -43,6 +47,7 @@
private final ProcessingEnvironment mEnv;
private final IntrospectionHelper mHelper;
private final DocumentModel mModel;
+ private final Set<ClassName> mDocumentTypesAccumulator = new HashSet<>();
public static void generate(
@NonNull ProcessingEnvironment env,
@@ -73,17 +78,55 @@
.addStatement("return SCHEMA_NAME")
.build());
+ CodeBlock schemaInitializer = createSchemaInitializerGetDocumentTypes();
+
classBuilder.addMethod(
MethodSpec.methodBuilder("getSchema")
.addModifiers(Modifier.PUBLIC)
.returns(mHelper.getAppSearchClass("AppSearchSchema"))
.addAnnotation(Override.class)
.addException(mHelper.getAppSearchExceptionClass())
- .addStatement("return $L", createSchemaInitializer())
+ .addStatement("return $L", schemaInitializer)
.build());
+
+ classBuilder.addMethod(createNestedClassesMethod());
}
- private CodeBlock createSchemaInitializer() throws ProcessingException {
+ @NonNull
+ private MethodSpec createNestedClassesMethod() {
+ TypeName setOfClasses = ParameterizedTypeName.get(ClassName.get("java.util", "List"),
+ ParameterizedTypeName.get(ClassName.get(Class.class),
+ WildcardTypeName.subtypeOf(Object.class)));
+
+ TypeName arraySetOfClasses =
+ ParameterizedTypeName.get(ClassName.get("java.util", "ArrayList"),
+ ParameterizedTypeName.get(ClassName.get(Class.class),
+ WildcardTypeName.subtypeOf(Object.class)));
+
+ MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("getNestedDocumentClasses")
+ .addModifiers(Modifier.PUBLIC)
+ .returns(setOfClasses)
+ .addAnnotation(Override.class)
+ .addException(mHelper.getAppSearchExceptionClass());
+
+ if (mDocumentTypesAccumulator.isEmpty()) {
+ methodBuilder.addStatement("return $T.emptyList()",
+ ClassName.get("java.util", "Collections"));
+ } else {
+ methodBuilder.addStatement("$T classSet = new $T()", setOfClasses, arraySetOfClasses);
+ for (ClassName className : mDocumentTypesAccumulator) {
+ methodBuilder.addStatement("classSet.add($T.class)", className);
+ }
+ methodBuilder.addStatement("return classSet").build();
+ }
+ return methodBuilder.build();
+ }
+
+ /**
+ * This method accumulates Document-type properties in mDocumentTypesAccumulator by calling
+ * {@link #createPropertySchema}.
+ */
+ private CodeBlock createSchemaInitializerGetDocumentTypes() throws ProcessingException {
CodeBlock.Builder codeBlock = CodeBlock.builder()
.add("new $T(SCHEMA_NAME)", mHelper.getAppSearchClass("AppSearchSchema", "Builder"))
.indent();
@@ -94,6 +137,7 @@
return codeBlock.build();
}
+ /** This method accumulates Document-type properties in mDocumentTypesAccumulator. */
private CodeBlock createPropertySchema(@NonNull VariableElement property)
throws ProcessingException {
AnnotationMirror annotation = mModel.getPropertyAnnotation(property);
@@ -166,6 +210,7 @@
propertyClass.nestedClass("Builder"),
propertyName,
documentFactoryClass);
+ mDocumentTypesAccumulator.add(documentClass);
} else {
codeBlock.add("new $T($S)", propertyClass.nestedClass("Builder"), propertyName);
}
@@ -227,6 +272,28 @@
}
codeBlock.add("\n.setIndexingType($T)", indexingEnum);
+ int joinableValueType = Integer.parseInt(params.get("joinableValueType").toString());
+ ClassName joinableEnum;
+ if (joinableValueType == 0) { // JOINABLE_VALUE_TYPE_NONE
+ joinableEnum = mHelper.getAppSearchClass(
+ "AppSearchSchema", "StringPropertyConfig", "JOINABLE_VALUE_TYPE_NONE");
+
+ } else if (joinableValueType == 1) { // JOINABLE_VALUE_TYPE_QUALIFIED_ID
+ if (repeated) {
+ throw new ProcessingException(
+ "Joinable value type " + joinableValueType + " not allowed on repeated "
+ + "properties.", property);
+
+ }
+ joinableEnum = mHelper.getAppSearchClass(
+ "AppSearchSchema", "StringPropertyConfig",
+ "JOINABLE_VALUE_TYPE_QUALIFIED_ID");
+ } else {
+ throw new ProcessingException(
+ "Unknown joinable value type " + joinableValueType, property);
+ }
+ codeBlock.add("\n.setJoinableValueType($T)", joinableEnum);
+
} else if (isPropertyDocument) {
if (params.containsKey("indexNestedProperties")) {
boolean indexNestedProperties = Boolean.parseBoolean(
diff --git a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
index b36c5877..43e24214 100644
--- a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
+++ b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
@@ -1190,6 +1190,38 @@
}
@Test
+ public void testStringPropertyJoinableType() throws Exception {
+ Compilation compilation = compile(
+ "import java.util.*;\n"
+ + "@Document\n"
+ + "public class Gift {\n"
+ + " @Document.Namespace String namespace;\n"
+ + " @Document.Id String id;\n"
+ + " @Document.StringProperty(joinableValueType=1)\n"
+ + " String object;\n"
+ + "}\n");
+
+ assertThat(compilation).succeededWithoutWarnings();
+ checkEqualsGolden("Gift.java");
+ }
+
+ @Test
+ public void testRepeatedPropertyJoinableType_throwsError() throws Exception {
+ Compilation compilation = compile(
+ "import java.util.*;\n"
+ + "@Document\n"
+ + "public class Gift {\n"
+ + " @Document.Namespace String namespace;\n"
+ + " @Document.Id String id;\n"
+ + " @Document.StringProperty(joinableValueType=1)\n"
+ + " List<String> object;\n"
+ + "}\n");
+
+ assertThat(compilation).hadErrorContaining(
+ "Joinable value type 1 not allowed on repeated properties.");
+ }
+
+ @Test
public void testPropertyName() throws Exception {
Compilation compilation = compile(
"import java.util.*;\n"
@@ -1504,6 +1536,44 @@
checkEqualsGolden("Gift.java");
}
+ @Test
+ public void testMultipleNesting() throws Exception {
+ Compilation compilation = compile(
+ "import java.util.*;\n"
+ + "@Document\n"
+ + "public class Gift {\n"
+ + " @Document.Id String id;\n"
+ + " @Document.Namespace String namespace;\n"
+ + " @Document.DocumentProperty Middle middleContentA;\n"
+ + " @Document.DocumentProperty Middle middleContentB;\n"
+ + "}\n"
+ + "\n"
+ + "@Document\n"
+ + "class Middle {\n"
+ + " @Document.Id String id;\n"
+ + " @Document.Namespace String namespace;\n"
+ + " @Document.DocumentProperty Inner innerContentA;\n"
+ + " @Document.DocumentProperty Inner innerContentB;\n"
+ + "}\n"
+ + "@Document\n"
+ + "class Inner {\n"
+ + " @Document.Id String id;\n"
+ + " @Document.Namespace String namespace;\n"
+ + " @Document.StringProperty String contents;\n"
+ + "}\n");
+
+ assertThat(compilation).succeededWithoutWarnings();
+ checkEqualsGolden("Gift.java");
+
+ // Check that Gift contains Middle, Middle contains Inner, and Inner returns empty
+ checkResultContains(/* className= */ "Gift.java",
+ /* content= */ "classSet.add(Middle.class);\n return classSet;");
+ checkResultContains(/* className= */ "Middle.java",
+ /* content= */ "classSet.add(Inner.class);\n return classSet;");
+ checkResultContains(/* className= */ "Inner.java",
+ /* content= */ "return Collections.emptyList();");
+ }
+
private Compilation compile(String classBody) {
return compile("Gift", classBody);
}
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
index 094d178..a49931b 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
@@ -5,12 +5,15 @@
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
import java.lang.Boolean;
+import java.lang.Class;
import java.lang.Double;
import java.lang.Float;
import java.lang.Integer;
import java.lang.Long;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -29,6 +32,7 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.LongPropertyConfig.Builder("integerProp")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -54,6 +58,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_field.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_field.JAVA
index 36930bc1..3f9ad76 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_field.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_field.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_getter.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_getter.JAVA
index 35fdea2..1d97d08 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_getter.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_getter.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.getId(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAutoValueDocument.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAutoValueDocument.JAVA
index ab783ed..fc27579 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAutoValueDocument.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAutoValueDocument.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,17 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace(), document.id(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
index 0f275a7..d7954e1 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
@@ -4,10 +4,12 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Float;
import java.lang.Override;
import java.lang.String;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Generated;
@@ -27,11 +29,13 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("repeatNoReq")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.DoublePropertyConfig.Builder("req")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
@@ -43,6 +47,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testDifferentTypeName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testDifferentTypeName.JAVA
index 7a7bca6..eb8b33b 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testDifferentTypeName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testDifferentTypeName.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,6 +27,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testGetterAndSetterFunctions_withFieldName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testGetterAndSetterFunctions_withFieldName.JAVA
index aa566b3..3096bdc 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testGetterAndSetterFunctions_withFieldName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testGetterAndSetterFunctions_withFieldName.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
index 810a16f..acad761 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,21 +27,29 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("indexExact")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("indexPrefix")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
index ac333bc..d55520d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,17 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift.InnerGift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testLongPropertyIndexingType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testLongPropertyIndexingType.JAVA
index c754aef..abe3acb 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testLongPropertyIndexingType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testLongPropertyIndexingType.JAVA
@@ -4,10 +4,13 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Integer;
import java.lang.Long;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -66,6 +69,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNestedAutoValueDocument.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNestedAutoValueDocument.JAVA
index 31c563e..856821f 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNestedAutoValueDocument.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNestedAutoValueDocument.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,6 +27,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift.A document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace(), document.id(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNesting.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNesting.JAVA
new file mode 100644
index 0000000..7d8acd0
--- /dev/null
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testMultipleNesting.JAVA
@@ -0,0 +1,82 @@
+package com.example.appsearch;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.DocumentClassFactory;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.appsearch.compiler.AppSearchCompiler")
+public final class $$__AppSearch__Gift implements DocumentClassFactory<Gift> {
+ public static final String SCHEMA_NAME = "Gift";
+
+ @Override
+ public String getSchemaName() {
+ return SCHEMA_NAME;
+ }
+
+ @Override
+ public AppSearchSchema getSchema() throws AppSearchException {
+ return new AppSearchSchema.Builder(SCHEMA_NAME)
+ .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder("middleContentA", $$__AppSearch__Middle.SCHEMA_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setShouldIndexNestedProperties(false)
+ .build())
+ .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder("middleContentB", $$__AppSearch__Middle.SCHEMA_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setShouldIndexNestedProperties(false)
+ .build())
+ .build();
+ }
+
+ @Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ List<Class<?>> classSet = new ArrayList<Class<?>>();
+ classSet.add(Middle.class);
+ return classSet;
+ }
+
+ @Override
+ public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
+ GenericDocument.Builder<?> builder =
+ new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
+ Middle middleContentACopy = document.middleContentA;
+ if (middleContentACopy != null) {
+ GenericDocument middleContentAConv = GenericDocument.fromDocumentClass(middleContentACopy);
+ builder.setPropertyDocument("middleContentA", middleContentAConv);
+ }
+ Middle middleContentBCopy = document.middleContentB;
+ if (middleContentBCopy != null) {
+ GenericDocument middleContentBConv = GenericDocument.fromDocumentClass(middleContentBCopy);
+ builder.setPropertyDocument("middleContentB", middleContentBConv);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Gift fromGenericDocument(GenericDocument genericDoc) throws AppSearchException {
+ String idConv = genericDoc.getId();
+ String namespaceConv = genericDoc.getNamespace();
+ GenericDocument middleContentACopy = genericDoc.getPropertyDocument("middleContentA");
+ Middle middleContentAConv = null;
+ if (middleContentACopy != null) {
+ middleContentAConv = middleContentACopy.toDocumentClass(Middle.class);
+ }
+ GenericDocument middleContentBCopy = genericDoc.getPropertyDocument("middleContentB");
+ Middle middleContentBConv = null;
+ if (middleContentBCopy != null) {
+ middleContentBConv = middleContentBCopy.toDocumentClass(Middle.class);
+ }
+ Gift document = new Gift();
+ document.id = idConv;
+ document.namespace = namespaceConv;
+ document.middleContentA = middleContentAConv;
+ document.middleContentB = middleContentBConv;
+ return document;
+ }
+}
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testNestedDocumentsIndexing.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testNestedDocumentsIndexing.JAVA
index c31f620..b24a19b 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testNestedDocumentsIndexing.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testNestedDocumentsIndexing.JAVA
@@ -4,6 +4,7 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.ArrayList;
@@ -51,6 +52,13 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ List<Class<?>> classSet = new ArrayList<Class<?>>();
+ classSet.add(GiftContent.class);
+ return classSet;
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testOneBadConstructor.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testOneBadConstructor.JAVA
index f0d8cb5..f3cca50 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testOneBadConstructor.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testOneBadConstructor.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,6 +27,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.getNamespace(), document.getId(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
index f6672c0..2a730d5 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,17 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
index f07c03b..44d3db6 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_isGetterForBoolean.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_isGetterForBoolean.JAVA
index 9e828fd..a676f5c 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_isGetterForBoolean.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_isGetterForBoolean.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -27,6 +30,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
index f46d524..89b7dbb 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
@@ -4,12 +4,14 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Integer;
import java.lang.Override;
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Generated;
@@ -29,6 +31,7 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.LongPropertyConfig.Builder("setOfInt")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
@@ -44,6 +47,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testStringPropertyJoinableType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testStringPropertyJoinableType.JAVA
new file mode 100644
index 0000000..722ebf59
--- /dev/null
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testStringPropertyJoinableType.JAVA
@@ -0,0 +1,66 @@
+package com.example.appsearch;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.DocumentClassFactory;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.appsearch.compiler.AppSearchCompiler")
+public final class $$__AppSearch__Gift implements DocumentClassFactory<Gift> {
+ public static final String SCHEMA_NAME = "Gift";
+
+ @Override
+ public String getSchemaName() {
+ return SCHEMA_NAME;
+ }
+
+ @Override
+ public AppSearchSchema getSchema() throws AppSearchException {
+ return new AppSearchSchema.Builder(SCHEMA_NAME)
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("object")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .build())
+ .build();
+ }
+
+ @Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
+ GenericDocument.Builder<?> builder =
+ new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
+ String objectCopy = document.object;
+ if (objectCopy != null) {
+ builder.setPropertyString("object", objectCopy);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Gift fromGenericDocument(GenericDocument genericDoc) throws AppSearchException {
+ String idConv = genericDoc.getId();
+ String namespaceConv = genericDoc.getNamespace();
+ String[] objectCopy = genericDoc.getPropertyStringArray("object");
+ String objectConv = null;
+ if (objectCopy != null && objectCopy.length != 0) {
+ objectConv = objectCopy[0];
+ }
+ Gift document = new Gift();
+ document.namespace = namespaceConv;
+ document.id = idConv;
+ document.object = objectConv;
+ return document;
+ }
+}
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
index de3177f..c30c660 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -34,6 +37,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass.JAVA
index ef1410d..9694ee2 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,13 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("sender")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder("foo")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -37,6 +42,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift.FooGift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassPojoAncestor.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassPojoAncestor.JAVA
index f51fa57..8f600f6 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassPojoAncestor.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassPojoAncestor.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,13 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("sender")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder("foo")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -37,6 +42,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift.FooGift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassWithPrivateFields.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassWithPrivateFields.JAVA
index 3fed9e3..d7feadb 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassWithPrivateFields.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClassWithPrivateFields.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,21 +27,29 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("receiver")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("sender")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.getNamespace(), document.getId(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_changeSchemaName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_changeSchemaName.JAVA
index 812463a..08e306c 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_changeSchemaName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_changeSchemaName.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,16 +27,23 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("sender")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_multipleChangedSchemaNames.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_multipleChangedSchemaNames.JAVA
index b77c03b..ac5dcb2 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_multipleChangedSchemaNames.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuperClass_multipleChangedSchemaNames.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,11 +27,13 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("sender")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder("foo")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -37,6 +42,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(FooGift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
index d7e9290..0643cf9 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
@@ -6,6 +6,7 @@
import androidx.appsearch.exceptions.AppSearchException;
import java.lang.Boolean;
import java.lang.Byte;
+import java.lang.Class;
import java.lang.Double;
import java.lang.Float;
import java.lang.Integer;
@@ -54,6 +55,7 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder("collectGift", $$__AppSearch__Gift.SCHEMA_NAME)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
@@ -103,6 +105,7 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder("arrGift", $$__AppSearch__Gift.SCHEMA_NAME)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
@@ -112,6 +115,7 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.LongPropertyConfig.Builder("boxLong")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -158,6 +162,13 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ List<Class<?>> classSet = new ArrayList<Class<?>>();
+ classSet.add(Gift.class);
+ return classSet;
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
index 678c212..15e72e7 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -24,66 +27,83 @@
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokPlainInvalid")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokVerbatimInvalid")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokRfc822Invalid")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokNone")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokPlain")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokVerbatim")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokRfc822")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_RFC822)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokNonePrefix")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokPlainPrefix")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokVerbatimPrefix")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("tokRfc822Prefix")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_RFC822)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
.build())
.build();
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleConventions.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleConventions.JAVA
index b7f69fc..cea4e5d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleConventions.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleConventions.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -40,6 +43,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.getId(), SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleSetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleSetters.JAVA
index f07c03b..44d3db6 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleSetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_multipleSetters.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_usableFactoryMethod_unusableConstructor.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_usableFactoryMethod_unusableConstructor.JAVA
index 34c2b72..e356243 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_usableFactoryMethod_unusableConstructor.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_usableFactoryMethod_unusableConstructor.JAVA
@@ -4,8 +4,11 @@
import androidx.appsearch.app.DocumentClassFactory;
import androidx.appsearch.app.GenericDocument;
import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
import java.lang.Override;
import java.lang.String;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.processing.Generated;
@Generated("androidx.appsearch.compiler.AppSearchCompiler")
@@ -28,6 +31,11 @@
}
@Override
+ public List<Class<?>> getNestedDocumentClasses() throws AppSearchException {
+ return Collections.emptyList();
+ }
+
+ @Override
public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
GenericDocument.Builder<?> builder =
new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/resources/robolectric.properties b/benchmark/baseline-profile-gradle-plugin/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/IsolationActivity.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/IsolationActivity.kt
index 475d34f..0f716e2 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/IsolationActivity.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/IsolationActivity.kt
@@ -53,6 +53,7 @@
setContentView(R.layout.isolation_activity)
// disable launch animation
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
if (firstInit) {
@@ -117,6 +118,7 @@
public fun actuallyFinish() {
// disable close animation
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
super.finish()
}
diff --git a/biometric/biometric/src/test/resources/robolectric.properties b/biometric/biometric/src/test/resources/robolectric.properties
index 7946f01..69fde47 100644
--- a/biometric/biometric/src/test/resources/robolectric.properties
+++ b/biometric/biometric/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/browser/browser/src/test/resources/robolectric.properties b/browser/browser/src/test/resources/robolectric.properties
index 7946f01..69fde47 100644
--- a/browser/browser/src/test/resources/robolectric.properties
+++ b/browser/browser/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
index 47d27e4..d9304a9 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -34,7 +34,7 @@
* Either an integer value or a pre-release platform code, prefixed with "android-" (ex.
* "android-28" or "android-Q") as you would see within the SDK's platforms directory.
*/
- const val COMPILE_SDK_VERSION = "android-33-ext5"
+ const val COMPILE_SDK_VERSION = "android-34"
/**
* The Android SDK version to use for targetSdkVersion meta-data.
@@ -47,7 +47,7 @@
* order for tests to run on devices running released versions of the Android OS. If this is
* set to a pre-release version, tests will only be able to run on pre-release devices.
*/
- const val TARGET_SDK_VERSION = 33
+ const val TARGET_SDK_VERSION = 34
/**
* Returns the build tools version that should be used for the project.
diff --git a/busytown/impl/check_translations.sh b/busytown/impl/check_translations.sh
index 35501ad..0f9e440 100755
--- a/busytown/impl/check_translations.sh
+++ b/busytown/impl/check_translations.sh
@@ -20,6 +20,7 @@
find . \
\( \
-iname '*sample*' \
+ -o -iname '*demo*' \
-o -iname '*donottranslate*' \
-o -iname '*debug*' \
-o -iname '*test*' \
diff --git a/camera/camera-camera2-pipe-integration/src/test/resources/robolectric.properties b/camera/camera-camera2-pipe-integration/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/camera/camera-camera2/src/test/resources/robolectric.properties b/camera/camera-camera2/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/camera/camera-camera2/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/camera/camera-core/src/test/resources/robolectric.properties b/camera/camera-core/src/test/resources/robolectric.properties
index 7946f01..69fde47 100644
--- a/camera/camera-core/src/test/resources/robolectric.properties
+++ b/camera/camera-core/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/camera/camera-video/lint-baseline.xml b/camera/camera-video/lint-baseline.xml
index e8b6b9c..303aa35 100644
--- a/camera/camera-video/lint-baseline.xml
+++ b/camera/camera-video/lint-baseline.xml
@@ -37,4 +37,13 @@
file="src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt"/>
</issue>
+ <issue
+ id="IgnoreClassLevelDetector"
+ message="@Ignore should not be used at the class level. Move the annotation to each test individually."
+ errorLine1="@Ignore("b/274840083")"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/test/java/androidx/camera/video/internal/audio/AudioSourceTest.kt"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-view/src/test/resources/robolectric.properties b/camera/camera-view/src/test/resources/robolectric.properties
index 7946f01..69fde47 100644
--- a/camera/camera-view/src/test/resources/robolectric.properties
+++ b/camera/camera-view/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index ac0e121..b3c356c 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -604,21 +604,29 @@
// For testing
// -----------------
+ /**
+ */
@VisibleForTesting
LifecycleCameraController getCameraController() {
return mCameraController;
}
+ /**
+ */
@VisibleForTesting
void setWrappedAnalyzer(@Nullable ImageAnalysis.Analyzer analyzer) {
mWrappedAnalyzer = analyzer;
}
+ /**
+ */
@VisibleForTesting
PreviewView getPreviewView() {
return mPreviewView;
}
+ /**
+ */
@VisibleForTesting
int getSensorRotation() {
return mRotation;
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
index d250fc8..4f5c8ae7 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceEffect.kt
@@ -32,4 +32,4 @@
fun release() {
processor.release()
}
-}
\ No newline at end of file
+}
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml b/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
index abdbd23..89a1fe8 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/layout/camera_controller_view.xml
@@ -173,4 +173,4 @@
android:id="@+id/torch_state_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
index b850fd4..d16f41d 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
@@ -40,4 +40,4 @@
android:id="@+id/mlkit"
android:title="@string/mlkit"
app:showAsAction="never" />
-</menu>
\ No newline at end of file
+</menu>
diff --git a/car/app/app-automotive/src/test/resources/robolectric.properties b/car/app/app-automotive/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/car/app/app-automotive/src/test/resources/robolectric.properties
+++ b/car/app/app-automotive/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/car/app/app-projected/src/test/resources/robolectric.properties b/car/app/app-projected/src/test/resources/robolectric.properties
index 7946f01..69fde47 100644
--- a/car/app/app-projected/src/test/resources/robolectric.properties
+++ b/car/app/app-projected/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/car/app/app-testing/src/test/resources/robolectric.properties b/car/app/app-testing/src/test/resources/robolectric.properties
index 71111c5..69fde47 100644
--- a/car/app/app-testing/src/test/resources/robolectric.properties
+++ b/car/app/app-testing/src/test/resources/robolectric.properties
@@ -1,17 +1,3 @@
-#
-# 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.
-#
-
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/car/app/app/src/test/resources/robolectric.properties b/car/app/app/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/car/app/app/src/test/resources/robolectric.properties
+++ b/car/app/app/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/compose/animation/animation-core/api/1.5.0-beta01.txt b/compose/animation/animation-core/api/1.5.0-beta01.txt
index 595b3e9..1bb4425 100644
--- a/compose/animation/animation-core/api/1.5.0-beta01.txt
+++ b/compose/animation/animation-core/api/1.5.0-beta01.txt
@@ -333,9 +333,6 @@
property public static final androidx.compose.animation.core.Easing LinearOutSlowInEasing;
}
- @kotlin.RequiresOptIn(message="This is an experimental animation API for Transition. It may change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTransitionApi {
- }
-
public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
}
@@ -435,9 +432,6 @@
method @androidx.compose.runtime.Composable public static androidx.compose.animation.core.InfiniteTransition rememberInfiniteTransition(optional String label);
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface InternalAnimationApi {
- }
-
@androidx.compose.runtime.Immutable public final class KeyframesSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
ctor public KeyframesSpec(androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config);
method public androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> getConfig();
@@ -621,7 +615,6 @@
method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
- method @androidx.compose.animation.core.ExperimentalTransitionApi @androidx.compose.runtime.Composable public static inline <S, T> androidx.compose.animation.core.Transition<T> createChildTransition(androidx.compose.animation.core.Transition<S>, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> transformToChildState);
method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(androidx.compose.animation.core.MutableTransitionState<T> transitionState, optional String? label);
method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(T targetState, optional String? label);
}
diff --git a/compose/animation/animation-core/api/public_plus_experimental_1.5.0-beta01.txt b/compose/animation/animation-core/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..595b3e9
--- /dev/null
+++ b/compose/animation/animation-core/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,768 @@
+// Signature format: 4.0
+package androidx.compose.animation.core {
+
+ public final class Animatable<T, V extends androidx.compose.animation.core.AnimationVector> {
+ ctor @Deprecated public Animatable(T initialValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional T? visibilityThreshold);
+ ctor public Animatable(T initialValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional T? visibilityThreshold, optional String label);
+ method public suspend Object? animateDecay(T initialVelocity, androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>>);
+ method public suspend Object? animateTo(T targetValue, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T initialVelocity, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Animatable<T,V>,kotlin.Unit>? block, optional kotlin.coroutines.Continuation<? super androidx.compose.animation.core.AnimationResult<T,V>>);
+ method public androidx.compose.runtime.State<T> asState();
+ method public String getLabel();
+ method public T? getLowerBound();
+ method public T getTargetValue();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T? getUpperBound();
+ method public T getValue();
+ method public T getVelocity();
+ method public V getVelocityVector();
+ method public boolean isRunning();
+ method public suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? stop(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void updateBounds(optional T? lowerBound, optional T? upperBound);
+ property public final boolean isRunning;
+ property public final String label;
+ property public final T? lowerBound;
+ property public final T targetValue;
+ property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ property public final T? upperBound;
+ property public final T value;
+ property public final T velocity;
+ property public final V velocityVector;
+ }
+
+ public final class AnimatableKt {
+ method public static androidx.compose.animation.core.Animatable<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> Animatable(float initialValue, optional float visibilityThreshold);
+ }
+
+ public final class AnimateAsStateKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDpAsState(float targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Dp,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDpAsState(float targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.Dp> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Dp,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> animateFloatAsState(float targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional float visibilityThreshold, optional String label, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> animateFloatAsState(float targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional float visibilityThreshold, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Integer> animateIntAsState(int targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Integer> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Integer> animateIntAsState(int targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Integer> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffsetAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffsetAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntOffset,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSizeAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSizeAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffsetAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Offset> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffsetAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Offset> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRectAsState(androidx.compose.ui.geometry.Rect targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Rect> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Rect,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRectAsState(androidx.compose.ui.geometry.Rect targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Rect> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Rect,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSizeAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Size> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Size,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSizeAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Size> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Size,kotlin.Unit>? finishedListener);
+ method @androidx.compose.runtime.Composable public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValueAsState(T targetValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T? visibilityThreshold, optional String label, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValueAsState(T targetValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional T? visibilityThreshold, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? finishedListener);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
+ method public long getDurationNanos();
+ method public T getTargetValue();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public default boolean isFinishedFromNanos(long playTimeNanos);
+ method public boolean isInfinite();
+ property public abstract long durationNanos;
+ property public abstract boolean isInfinite;
+ property public abstract T targetValue;
+ property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ }
+
+ public final class AnimationConstants {
+ field public static final int DefaultDurationMillis = 300; // 0x12c
+ field public static final androidx.compose.animation.core.AnimationConstants INSTANCE;
+ field public static final long UnspecifiedTime = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public enum AnimationEndReason {
+ method public static androidx.compose.animation.core.AnimationEndReason valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.animation.core.AnimationEndReason[] values();
+ enum_constant public static final androidx.compose.animation.core.AnimationEndReason BoundReached;
+ enum_constant public static final androidx.compose.animation.core.AnimationEndReason Finished;
+ }
+
+ public final class AnimationKt {
+ method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T targetValue, T initialVelocity);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> T getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
+ }
+
+ public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
+ ctor public AnimationResult(androidx.compose.animation.core.AnimationState<T,V> endState, androidx.compose.animation.core.AnimationEndReason endReason);
+ method public androidx.compose.animation.core.AnimationEndReason getEndReason();
+ method public androidx.compose.animation.core.AnimationState<T,V> getEndState();
+ property public final androidx.compose.animation.core.AnimationEndReason endReason;
+ property public final androidx.compose.animation.core.AnimationState<T,V> endState;
+ }
+
+ public final class AnimationScope<T, V extends androidx.compose.animation.core.AnimationVector> {
+ method public void cancelAnimation();
+ method public long getFinishedTimeNanos();
+ method public long getLastFrameTimeNanos();
+ method public long getStartTimeNanos();
+ method public T getTargetValue();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValue();
+ method public T getVelocity();
+ method public V getVelocityVector();
+ method public boolean isRunning();
+ method public androidx.compose.animation.core.AnimationState<T,V> toAnimationState();
+ property public final long finishedTimeNanos;
+ property public final boolean isRunning;
+ property public final long lastFrameTimeNanos;
+ property public final long startTimeNanos;
+ property public final T targetValue;
+ property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ property public final T value;
+ property public final T velocity;
+ property public final V velocityVector;
+ }
+
+ public interface AnimationSpec<T> {
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ }
+
+ public final class AnimationSpecKt {
+ method @Deprecated @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.InfiniteRepeatableSpec<T> infiniteRepeatable(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.InfiniteRepeatableSpec<T> infiniteRepeatable(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.KeyframesSpec<T> keyframes(kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T>,kotlin.Unit> init);
+ method @Deprecated @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.RepeatableSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.RepeatableSpec<T> repeatable(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SnapSpec<T> snap(optional int delayMillis);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.SpringSpec<T> spring(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
+ method @androidx.compose.runtime.Stable public static <T> androidx.compose.animation.core.TweenSpec<T> tween(optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
+ }
+
+ public final class AnimationState<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.runtime.State<T> {
+ ctor public AnimationState(androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, optional V? initialVelocityVector, optional long lastFrameTimeNanos, optional long finishedTimeNanos, optional boolean isRunning);
+ method public long getFinishedTimeNanos();
+ method public long getLastFrameTimeNanos();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValue();
+ method public T getVelocity();
+ method public V getVelocityVector();
+ method public boolean isRunning();
+ property public final long finishedTimeNanos;
+ property public final boolean isRunning;
+ property public final long lastFrameTimeNanos;
+ property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ property public T value;
+ property public final T velocity;
+ property public final V velocityVector;
+ }
+
+ public final class AnimationStateKt {
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.AnimationState<T,V> AnimationState(androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T initialVelocity, optional long lastFrameTimeNanos, optional long finishedTimeNanos, optional boolean isRunning);
+ method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> AnimationState(float initialValue, optional float initialVelocity, optional long lastFrameTimeNanos, optional long finishedTimeNanos, optional boolean isRunning);
+ method public static androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> copy(androidx.compose.animation.core.AnimationState<java.lang.Float,androidx.compose.animation.core.AnimationVector1D>, optional float value, optional float velocity, optional long lastFrameTimeNanos, optional long finishedTimeNanos, optional boolean isRunning);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.AnimationState<T,V> copy(androidx.compose.animation.core.AnimationState<T,V>, optional T value, optional V? velocityVector, optional long lastFrameTimeNanos, optional long finishedTimeNanos, optional boolean isRunning);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> V createZeroVectorFrom(androidx.compose.animation.core.TwoWayConverter<T,V>, T value);
+ method public static boolean isFinished(androidx.compose.animation.core.AnimationState<?,?>);
+ }
+
+ public abstract sealed class AnimationVector {
+ }
+
+ public final class AnimationVector1D extends androidx.compose.animation.core.AnimationVector {
+ ctor public AnimationVector1D(float initVal);
+ method public float getValue();
+ property public final float value;
+ }
+
+ public final class AnimationVector2D extends androidx.compose.animation.core.AnimationVector {
+ ctor public AnimationVector2D(float v1, float v2);
+ method public float getV1();
+ method public float getV2();
+ property public final float v1;
+ property public final float v2;
+ }
+
+ public final class AnimationVector3D extends androidx.compose.animation.core.AnimationVector {
+ ctor public AnimationVector3D(float v1, float v2, float v3);
+ method public float getV1();
+ method public float getV2();
+ method public float getV3();
+ property public final float v1;
+ property public final float v2;
+ property public final float v3;
+ }
+
+ public final class AnimationVector4D extends androidx.compose.animation.core.AnimationVector {
+ ctor public AnimationVector4D(float v1, float v2, float v3, float v4);
+ method public float getV1();
+ method public float getV2();
+ method public float getV3();
+ method public float getV4();
+ property public final float v1;
+ property public final float v2;
+ property public final float v3;
+ property public final float v4;
+ }
+
+ public final class AnimationVectorsKt {
+ method public static androidx.compose.animation.core.AnimationVector1D AnimationVector(float v1);
+ method public static androidx.compose.animation.core.AnimationVector2D AnimationVector(float v1, float v2);
+ method public static androidx.compose.animation.core.AnimationVector3D AnimationVector(float v1, float v2, float v3);
+ method public static androidx.compose.animation.core.AnimationVector4D AnimationVector(float v1, float v2, float v3, float v4);
+ }
+
+ @androidx.compose.runtime.Immutable public final class CubicBezierEasing implements androidx.compose.animation.core.Easing {
+ ctor public CubicBezierEasing(float a, float b, float c, float d);
+ method public float transform(float fraction);
+ }
+
+ public final class DecayAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
+ ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T initialVelocity);
+ ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, V initialVelocityVector);
+ ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, V initialVelocityVector);
+ method public long getDurationNanos();
+ method public T getInitialValue();
+ method public V getInitialVelocityVector();
+ method public T getTargetValue();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public boolean isInfinite();
+ property public long durationNanos;
+ property public final T initialValue;
+ property public final V initialVelocityVector;
+ property public boolean isInfinite;
+ property public T targetValue;
+ property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ }
+
+ public interface DecayAnimationSpec<T> {
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter);
+ }
+
+ public final class DecayAnimationSpecKt {
+ method public static float calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>, float initialValue, float initialVelocity);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> T calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<T>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T initialVelocity);
+ method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> exponentialDecay(optional float frictionMultiplier, optional float absVelocityThreshold);
+ method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> generateDecayAnimationSpec(androidx.compose.animation.core.FloatDecayAnimationSpec);
+ }
+
+ public interface DurationBasedAnimationSpec<T> extends androidx.compose.animation.core.FiniteAnimationSpec<T> {
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ }
+
+ @androidx.compose.runtime.Stable public fun interface Easing {
+ method public float transform(float fraction);
+ }
+
+ public final class EasingFunctionsKt {
+ method public static androidx.compose.animation.core.Easing getEase();
+ method public static androidx.compose.animation.core.Easing getEaseIn();
+ method public static androidx.compose.animation.core.Easing getEaseInBack();
+ method public static androidx.compose.animation.core.Easing getEaseInBounce();
+ method public static androidx.compose.animation.core.Easing getEaseInCirc();
+ method public static androidx.compose.animation.core.Easing getEaseInCubic();
+ method public static androidx.compose.animation.core.Easing getEaseInElastic();
+ method public static androidx.compose.animation.core.Easing getEaseInExpo();
+ method public static androidx.compose.animation.core.Easing getEaseInOut();
+ method public static androidx.compose.animation.core.Easing getEaseInOutBack();
+ method public static androidx.compose.animation.core.Easing getEaseInOutBounce();
+ method public static androidx.compose.animation.core.Easing getEaseInOutCirc();
+ method public static androidx.compose.animation.core.Easing getEaseInOutCubic();
+ method public static androidx.compose.animation.core.Easing getEaseInOutElastic();
+ method public static androidx.compose.animation.core.Easing getEaseInOutExpo();
+ method public static androidx.compose.animation.core.Easing getEaseInOutQuad();
+ method public static androidx.compose.animation.core.Easing getEaseInOutQuart();
+ method public static androidx.compose.animation.core.Easing getEaseInOutQuint();
+ method public static androidx.compose.animation.core.Easing getEaseInOutSine();
+ method public static androidx.compose.animation.core.Easing getEaseInQuad();
+ method public static androidx.compose.animation.core.Easing getEaseInQuart();
+ method public static androidx.compose.animation.core.Easing getEaseInQuint();
+ method public static androidx.compose.animation.core.Easing getEaseInSine();
+ method public static androidx.compose.animation.core.Easing getEaseOut();
+ method public static androidx.compose.animation.core.Easing getEaseOutBack();
+ method public static androidx.compose.animation.core.Easing getEaseOutBounce();
+ method public static androidx.compose.animation.core.Easing getEaseOutCirc();
+ method public static androidx.compose.animation.core.Easing getEaseOutCubic();
+ method public static androidx.compose.animation.core.Easing getEaseOutElastic();
+ method public static androidx.compose.animation.core.Easing getEaseOutExpo();
+ method public static androidx.compose.animation.core.Easing getEaseOutQuad();
+ method public static androidx.compose.animation.core.Easing getEaseOutQuart();
+ method public static androidx.compose.animation.core.Easing getEaseOutQuint();
+ method public static androidx.compose.animation.core.Easing getEaseOutSine();
+ property public static final androidx.compose.animation.core.Easing Ease;
+ property public static final androidx.compose.animation.core.Easing EaseIn;
+ property public static final androidx.compose.animation.core.Easing EaseInBack;
+ property public static final androidx.compose.animation.core.Easing EaseInBounce;
+ property public static final androidx.compose.animation.core.Easing EaseInCirc;
+ property public static final androidx.compose.animation.core.Easing EaseInCubic;
+ property public static final androidx.compose.animation.core.Easing EaseInElastic;
+ property public static final androidx.compose.animation.core.Easing EaseInExpo;
+ property public static final androidx.compose.animation.core.Easing EaseInOut;
+ property public static final androidx.compose.animation.core.Easing EaseInOutBack;
+ property public static final androidx.compose.animation.core.Easing EaseInOutBounce;
+ property public static final androidx.compose.animation.core.Easing EaseInOutCirc;
+ property public static final androidx.compose.animation.core.Easing EaseInOutCubic;
+ property public static final androidx.compose.animation.core.Easing EaseInOutElastic;
+ property public static final androidx.compose.animation.core.Easing EaseInOutExpo;
+ property public static final androidx.compose.animation.core.Easing EaseInOutQuad;
+ property public static final androidx.compose.animation.core.Easing EaseInOutQuart;
+ property public static final androidx.compose.animation.core.Easing EaseInOutQuint;
+ property public static final androidx.compose.animation.core.Easing EaseInOutSine;
+ property public static final androidx.compose.animation.core.Easing EaseInQuad;
+ property public static final androidx.compose.animation.core.Easing EaseInQuart;
+ property public static final androidx.compose.animation.core.Easing EaseInQuint;
+ property public static final androidx.compose.animation.core.Easing EaseInSine;
+ property public static final androidx.compose.animation.core.Easing EaseOut;
+ property public static final androidx.compose.animation.core.Easing EaseOutBack;
+ property public static final androidx.compose.animation.core.Easing EaseOutBounce;
+ property public static final androidx.compose.animation.core.Easing EaseOutCirc;
+ property public static final androidx.compose.animation.core.Easing EaseOutCubic;
+ property public static final androidx.compose.animation.core.Easing EaseOutElastic;
+ property public static final androidx.compose.animation.core.Easing EaseOutExpo;
+ property public static final androidx.compose.animation.core.Easing EaseOutQuad;
+ property public static final androidx.compose.animation.core.Easing EaseOutQuart;
+ property public static final androidx.compose.animation.core.Easing EaseOutQuint;
+ property public static final androidx.compose.animation.core.Easing EaseOutSine;
+ }
+
+ public final class EasingKt {
+ method public static androidx.compose.animation.core.Easing getFastOutLinearInEasing();
+ method public static androidx.compose.animation.core.Easing getFastOutSlowInEasing();
+ method public static androidx.compose.animation.core.Easing getLinearEasing();
+ method public static androidx.compose.animation.core.Easing getLinearOutSlowInEasing();
+ property public static final androidx.compose.animation.core.Easing FastOutLinearInEasing;
+ property public static final androidx.compose.animation.core.Easing FastOutSlowInEasing;
+ property public static final androidx.compose.animation.core.Easing LinearEasing;
+ property public static final androidx.compose.animation.core.Easing LinearOutSlowInEasing;
+ }
+
+ @kotlin.RequiresOptIn(message="This is an experimental animation API for Transition. It may change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTransitionApi {
+ }
+
+ public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
+ }
+
+ public interface FloatDecayAnimationSpec {
+ method public float getAbsVelocityThreshold();
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ property public abstract float absVelocityThreshold;
+ }
+
+ public final class FloatExponentialDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
+ ctor public FloatExponentialDecaySpec(optional float frictionMultiplier, optional float absVelocityThreshold);
+ method public float getAbsVelocityThreshold();
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ property public float absVelocityThreshold;
+ }
+
+ public final class FloatSpringSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+ ctor public FloatSpringSpec(optional float dampingRatio, optional float stiffness, optional float visibilityThreshold);
+ method public float getDampingRatio();
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public float getStiffness();
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ property public final float dampingRatio;
+ property public final float stiffness;
+ }
+
+ public final class FloatTweenSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+ ctor public FloatTweenSpec(optional int duration, optional int delay, optional androidx.compose.animation.core.Easing easing);
+ method public int getDelay();
+ method public int getDuration();
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ property public final int delay;
+ property public final int duration;
+ }
+
+ public final class InfiniteAnimationPolicyKt {
+ method public static suspend inline <R> Object? withInfiniteAnimationFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend <R> Object? withInfiniteAnimationFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ }
+
+ public final class InfiniteRepeatableSpec<T> implements androidx.compose.animation.core.AnimationSpec<T> {
+ ctor @Deprecated public InfiniteRepeatableSpec(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ ctor public InfiniteRepeatableSpec(androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
+ method public long getInitialStartOffset();
+ method public androidx.compose.animation.core.RepeatMode getRepeatMode();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
+ property public final long initialStartOffset;
+ property public final androidx.compose.animation.core.RepeatMode repeatMode;
+ }
+
+ public final class InfiniteTransition {
+ method public java.util.List<androidx.compose.animation.core.InfiniteTransition.TransitionAnimationState<?,?>> getAnimations();
+ method public String getLabel();
+ property public final java.util.List<androidx.compose.animation.core.InfiniteTransition.TransitionAnimationState<?,?>> animations;
+ property public final String label;
+ }
+
+ public final class InfiniteTransition.TransitionAnimationState<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.runtime.State<T> {
+ method public androidx.compose.animation.core.TargetBasedAnimation<T,V> getAnimation();
+ method public androidx.compose.animation.core.AnimationSpec<T> getAnimationSpec();
+ method public String getLabel();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValue();
+ property public final androidx.compose.animation.core.TargetBasedAnimation<T,V> animation;
+ property public final androidx.compose.animation.core.AnimationSpec<T> animationSpec;
+ property public final String label;
+ property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ property public T value;
+ }
+
+ public final class InfiniteTransitionKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.InfiniteTransition, float initialValue, float targetValue, androidx.compose.animation.core.InfiniteRepeatableSpec<java.lang.Float> animationSpec);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.InfiniteTransition, float initialValue, float targetValue, androidx.compose.animation.core.InfiniteRepeatableSpec<java.lang.Float> animationSpec, optional String label);
+ method @Deprecated @androidx.compose.runtime.Composable public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.InfiniteTransition, T initialValue, T targetValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, androidx.compose.animation.core.InfiniteRepeatableSpec<T> animationSpec);
+ method @androidx.compose.runtime.Composable public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.InfiniteTransition, T initialValue, T targetValue, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, androidx.compose.animation.core.InfiniteRepeatableSpec<T> animationSpec, optional String label);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.animation.core.InfiniteTransition rememberInfiniteTransition();
+ method @androidx.compose.runtime.Composable public static androidx.compose.animation.core.InfiniteTransition rememberInfiniteTransition(optional String label);
+ }
+
+ @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface InternalAnimationApi {
+ }
+
+ @androidx.compose.runtime.Immutable public final class KeyframesSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+ ctor public KeyframesSpec(androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config);
+ method public androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> getConfig();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedKeyframesSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config;
+ }
+
+ public static final class KeyframesSpec.KeyframeEntity<T> {
+ }
+
+ public static final class KeyframesSpec.KeyframesSpecConfig<T> {
+ ctor public KeyframesSpec.KeyframesSpecConfig();
+ method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, int timeStamp);
+ method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> atFraction(T, float fraction);
+ method public int getDelayMillis();
+ method public int getDurationMillis();
+ method public void setDelayMillis(int);
+ method public void setDurationMillis(int);
+ method public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
+ property public final int delayMillis;
+ property public final int durationMillis;
+ }
+
+ public final class MutableTransitionState<S> {
+ ctor public MutableTransitionState(S initialState);
+ method public S getCurrentState();
+ method public S getTargetState();
+ method public boolean isIdle();
+ method public void setTargetState(S!);
+ property public final S currentState;
+ property public final boolean isIdle;
+ property public final S targetState;
+ }
+
+ public enum RepeatMode {
+ method public static androidx.compose.animation.core.RepeatMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.animation.core.RepeatMode[] values();
+ enum_constant public static final androidx.compose.animation.core.RepeatMode Restart;
+ enum_constant public static final androidx.compose.animation.core.RepeatMode Reverse;
+ }
+
+ @androidx.compose.runtime.Immutable public final class RepeatableSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
+ ctor @Deprecated public RepeatableSpec(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ ctor public RepeatableSpec(int iterations, androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method public androidx.compose.animation.core.DurationBasedAnimationSpec<T> getAnimation();
+ method public long getInitialStartOffset();
+ method public int getIterations();
+ method public androidx.compose.animation.core.RepeatMode getRepeatMode();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final androidx.compose.animation.core.DurationBasedAnimationSpec<T> animation;
+ property public final long initialStartOffset;
+ property public final int iterations;
+ property public final androidx.compose.animation.core.RepeatMode repeatMode;
+ }
+
+ @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+ ctor public SnapSpec(optional int delay);
+ method public int getDelay();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final int delay;
+ }
+
+ public final class Spring {
+ field public static final float DampingRatioHighBouncy = 0.2f;
+ field public static final float DampingRatioLowBouncy = 0.75f;
+ field public static final float DampingRatioMediumBouncy = 0.5f;
+ field public static final float DampingRatioNoBouncy = 1.0f;
+ field public static final float DefaultDisplacementThreshold = 0.01f;
+ field public static final androidx.compose.animation.core.Spring INSTANCE;
+ field public static final float StiffnessHigh = 10000.0f;
+ field public static final float StiffnessLow = 200.0f;
+ field public static final float StiffnessMedium = 1500.0f;
+ field public static final float StiffnessMediumLow = 400.0f;
+ field public static final float StiffnessVeryLow = 50.0f;
+ }
+
+ public final class SpringEstimationKt {
+ }
+
+ @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
+ ctor public SpringSpec(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
+ method public float getDampingRatio();
+ method public float getStiffness();
+ method public T? getVisibilityThreshold();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedSpringSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final float dampingRatio;
+ property public final float stiffness;
+ property public final T? visibilityThreshold;
+ }
+
+ @kotlin.jvm.JvmInline public final value class StartOffset {
+ ctor public StartOffset(int offsetMillis, optional int offsetType);
+ method public int getOffsetMillis();
+ method public int getOffsetType();
+ property public final int offsetMillis;
+ property public final int offsetType;
+ }
+
+ @kotlin.jvm.JvmInline public final value class StartOffsetType {
+ field public static final androidx.compose.animation.core.StartOffsetType.Companion Companion;
+ }
+
+ public static final class StartOffsetType.Companion {
+ method public int getDelay();
+ method public int getFastForward();
+ property public final int Delay;
+ property public final int FastForward;
+ }
+
+ public final class SuspendAnimationKt {
+ method public static suspend <T, V extends androidx.compose.animation.core.AnimationVector> Object? animate(androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T targetValue, optional T? initialVelocity, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, kotlin.jvm.functions.Function2<? super T,? super T,kotlin.Unit> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? animate(float initialValue, float targetValue, optional float initialVelocity, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,kotlin.Unit> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend <T, V extends androidx.compose.animation.core.AnimationVector> Object? animateDecay(androidx.compose.animation.core.AnimationState<T,V>, androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, optional boolean sequentialAnimation, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.AnimationScope<T,V>,kotlin.Unit> block, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? animateDecay(float initialValue, float initialVelocity, androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,kotlin.Unit> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend <T, V extends androidx.compose.animation.core.AnimationVector> Object? animateTo(androidx.compose.animation.core.AnimationState<T,V>, T targetValue, optional androidx.compose.animation.core.AnimationSpec<T> animationSpec, optional boolean sequentialAnimation, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.AnimationScope<T,V>,kotlin.Unit> block, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
+ ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T targetValue, optional V? initialVelocityVector);
+ method public long getDurationNanos();
+ method public T getInitialValue();
+ method public T getTargetValue();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public boolean isInfinite();
+ property public long durationNanos;
+ property public final T initialValue;
+ property public boolean isInfinite;
+ property public T targetValue;
+ property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ }
+
+ @androidx.compose.runtime.Stable public final class Transition<S> {
+ method public java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> getAnimations();
+ method public S getCurrentState();
+ method public String? getLabel();
+ method public androidx.compose.animation.core.Transition.Segment<S> getSegment();
+ method public S getTargetState();
+ method public long getTotalDurationNanos();
+ method public java.util.List<androidx.compose.animation.core.Transition<?>> getTransitions();
+ method public boolean isRunning();
+ property public final java.util.List<androidx.compose.animation.core.Transition<S>.TransitionAnimationState<?,?>> animations;
+ property public final S currentState;
+ property public final boolean isRunning;
+ property public final String? label;
+ property public final androidx.compose.animation.core.Transition.Segment<S> segment;
+ property public final S targetState;
+ property public final long totalDurationNanos;
+ property public final java.util.List<androidx.compose.animation.core.Transition<?>> transitions;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public static interface Transition.Segment<S> {
+ method public S getInitialState();
+ method public S getTargetState();
+ method public default infix boolean isTransitioningTo(S, S targetState);
+ property public abstract S initialState;
+ property public abstract S targetState;
+ }
+
+ @androidx.compose.runtime.Stable public final class Transition.TransitionAnimationState<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.runtime.State<T> {
+ method public androidx.compose.animation.core.TargetBasedAnimation<T,V> getAnimation();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<T> getAnimationSpec();
+ method public String getLabel();
+ method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
+ method public T getValue();
+ property public final androidx.compose.animation.core.TargetBasedAnimation<T,V> animation;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<T> animationSpec;
+ property public final String label;
+ property public final androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
+ property public T value;
+ }
+
+ public final class TransitionKt {
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> animateDp(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.Dp>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.Dp> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Float> animateFloat(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,java.lang.Float> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<java.lang.Integer> animateInt(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Integer>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,java.lang.Integer> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntOffset> animateIntOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntOffset> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.unit.IntSize> animateIntSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.unit.IntSize> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Offset> animateOffset(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Offset>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Offset> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
+ method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
+ method @androidx.compose.animation.core.ExperimentalTransitionApi @androidx.compose.runtime.Composable public static inline <S, T> androidx.compose.animation.core.Transition<T> createChildTransition(androidx.compose.animation.core.Transition<S>, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> transformToChildState);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(androidx.compose.animation.core.MutableTransitionState<T> transitionState, optional String? label);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(T targetState, optional String? label);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TweenSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+ ctor public TweenSpec(optional int durationMillis, optional int delay, optional androidx.compose.animation.core.Easing easing);
+ method public int getDelay();
+ method public int getDurationMillis();
+ method public androidx.compose.animation.core.Easing getEasing();
+ method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedTweenSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
+ property public final int delay;
+ property public final int durationMillis;
+ property public final androidx.compose.animation.core.Easing easing;
+ }
+
+ public interface TwoWayConverter<T, V extends androidx.compose.animation.core.AnimationVector> {
+ method public kotlin.jvm.functions.Function1<V,T> getConvertFromVector();
+ method public kotlin.jvm.functions.Function1<T,V> getConvertToVector();
+ property public abstract kotlin.jvm.functions.Function1<V,T> convertFromVector;
+ property public abstract kotlin.jvm.functions.Function1<T,V> convertToVector;
+ }
+
+ public final class VectorConvertersKt {
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TwoWayConverter<T,V> TwoWayConverter(kotlin.jvm.functions.Function1<? super T,? extends V> convertToVector, kotlin.jvm.functions.Function1<? super V,? extends T> convertFromVector);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Offset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Offset.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Rect,androidx.compose.animation.core.AnimationVector4D> getVectorConverter(androidx.compose.ui.geometry.Rect.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.geometry.Size,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.geometry.Size.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(androidx.compose.ui.unit.Dp.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.DpOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.DpOffset.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntOffset,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntOffset.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.unit.IntSize,androidx.compose.animation.core.AnimationVector2D> getVectorConverter(androidx.compose.ui.unit.IntSize.Companion);
+ method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.FloatCompanionObject);
+ method public static androidx.compose.animation.core.TwoWayConverter<java.lang.Integer,androidx.compose.animation.core.AnimationVector1D> getVectorConverter(kotlin.jvm.internal.IntCompanionObject);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public boolean isInfinite();
+ property public abstract boolean isInfinite;
+ }
+
+ public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
+ method public float getAbsVelocityThreshold();
+ method public long getDurationNanos(V initialValue, V initialVelocity);
+ method public V getTargetValue(V initialValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+ property public abstract float absVelocityThreshold;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
+ method public int getDelayMillis();
+ method public int getDurationMillis();
+ method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ property public abstract int delayMillis;
+ property public abstract int durationMillis;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+ method public default boolean isInfinite();
+ property public default boolean isInfinite;
+ }
+
+ public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
+ ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ }
+
+ public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+ ctor @Deprecated public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public boolean isInfinite();
+ property public boolean isInfinite;
+ }
+
+ public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+ ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, optional int delayMillis);
+ method public int getDelayMillis();
+ method public int getDurationMillis();
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ property public int delayMillis;
+ property public int durationMillis;
+ }
+
+ public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
+ ctor @Deprecated public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, optional androidx.compose.animation.core.RepeatMode repeatMode);
+ ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, optional androidx.compose.animation.core.RepeatMode repeatMode, optional long initialStartOffset);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ }
+
+ public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+ ctor public VectorizedSnapSpec(optional int delayMillis);
+ method public int getDelayMillis();
+ method public int getDurationMillis();
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ property public int delayMillis;
+ property public int durationMillis;
+ }
+
+ public final class VectorizedSpringSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
+ ctor public VectorizedSpringSpec(optional float dampingRatio, optional float stiffness, optional V? visibilityThreshold);
+ method public float getDampingRatio();
+ method public float getStiffness();
+ property public final float dampingRatio;
+ property public final float stiffness;
+ }
+
+ public final class VectorizedTweenSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+ ctor public VectorizedTweenSpec(optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
+ method public int getDelayMillis();
+ method public int getDurationMillis();
+ method public androidx.compose.animation.core.Easing getEasing();
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ property public int delayMillis;
+ property public int durationMillis;
+ property public final androidx.compose.animation.core.Easing easing;
+ }
+
+ public final class VisibilityThresholdsKt {
+ method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Offset.Companion);
+ method public static androidx.compose.ui.geometry.Rect getVisibilityThreshold(androidx.compose.ui.geometry.Rect.Companion);
+ method public static long getVisibilityThreshold(androidx.compose.ui.geometry.Size.Companion);
+ method public static float getVisibilityThreshold(androidx.compose.ui.unit.Dp.Companion);
+ method public static long getVisibilityThreshold(androidx.compose.ui.unit.DpOffset.Companion);
+ method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntOffset.Companion);
+ method public static long getVisibilityThreshold(androidx.compose.ui.unit.IntSize.Companion);
+ method public static int getVisibilityThreshold(kotlin.jvm.internal.IntCompanionObject);
+ }
+
+}
+
diff --git a/compose/animation/animation-core/api/restricted_1.5.0-beta01.txt b/compose/animation/animation-core/api/restricted_1.5.0-beta01.txt
index b96faf7..0d3bd05 100644
--- a/compose/animation/animation-core/api/restricted_1.5.0-beta01.txt
+++ b/compose/animation/animation-core/api/restricted_1.5.0-beta01.txt
@@ -333,9 +333,6 @@
property public static final androidx.compose.animation.core.Easing LinearOutSlowInEasing;
}
- @kotlin.RequiresOptIn(message="This is an experimental animation API for Transition. It may change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTransitionApi {
- }
-
public interface FiniteAnimationSpec<T> extends androidx.compose.animation.core.AnimationSpec<T> {
method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
}
@@ -435,9 +432,6 @@
method @androidx.compose.runtime.Composable public static androidx.compose.animation.core.InfiniteTransition rememberInfiniteTransition(optional String label);
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface InternalAnimationApi {
- }
-
@androidx.compose.runtime.Immutable public final class KeyframesSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
ctor public KeyframesSpec(androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config);
method public androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> getConfig();
@@ -622,7 +616,6 @@
method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Rect> animateRect(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Rect>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Rect> targetValueByState);
method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.geometry.Size> animateSize(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.geometry.Size>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.geometry.Size> targetValueByState);
method @androidx.compose.runtime.Composable public static inline <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> animateValue(androidx.compose.animation.core.Transition<S>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<T>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> targetValueByState);
- method @androidx.compose.animation.core.ExperimentalTransitionApi @androidx.compose.runtime.Composable public static inline <S, T> androidx.compose.animation.core.Transition<T> createChildTransition(androidx.compose.animation.core.Transition<S>, optional String label, kotlin.jvm.functions.Function1<? super S,? extends T> transformToChildState);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static <S, T> androidx.compose.animation.core.Transition<T> createChildTransitionInternal(androidx.compose.animation.core.Transition<S>, T initialState, T targetState, String childLabel);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static <S, T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.runtime.State<T> createTransitionAnimation(androidx.compose.animation.core.Transition<S>, T initialValue, T targetValue, androidx.compose.animation.core.FiniteAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, String label);
method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.Transition<T> updateTransition(androidx.compose.animation.core.MutableTransitionState<T> transitionState, optional String? label);
diff --git a/compose/animation/animation-graphics/api/1.5.0-beta01.txt b/compose/animation/animation-graphics/api/1.5.0-beta01.txt
index cf4d086..e6f50d0 100644
--- a/compose/animation/animation-graphics/api/1.5.0-beta01.txt
+++ b/compose/animation/animation-graphics/api/1.5.0-beta01.txt
@@ -1,35 +1 @@
// Signature format: 4.0
-package androidx.compose.animation.graphics {
-
- @kotlin.RequiresOptIn(message="This is an experimental animation graphics API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface ExperimentalAnimationGraphicsApi {
- }
-
-}
-
-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(androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion, @DrawableRes int id);
- }
-
-}
-
-package androidx.compose.animation.graphics.vector {
-
- @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();
- 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 {
- }
-
-}
-
diff --git a/compose/animation/animation-graphics/api/public_plus_experimental_1.5.0-beta01.txt b/compose/animation/animation-graphics/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..cf4d086
--- /dev/null
+++ b/compose/animation/animation-graphics/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 4.0
+package androidx.compose.animation.graphics {
+
+ @kotlin.RequiresOptIn(message="This is an experimental animation graphics API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface ExperimentalAnimationGraphicsApi {
+ }
+
+}
+
+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(androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion, @DrawableRes int id);
+ }
+
+}
+
+package androidx.compose.animation.graphics.vector {
+
+ @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();
+ 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 {
+ }
+
+}
+
diff --git a/compose/animation/animation-graphics/api/restricted_1.5.0-beta01.txt b/compose/animation/animation-graphics/api/restricted_1.5.0-beta01.txt
index cf4d086..e6f50d0 100644
--- a/compose/animation/animation-graphics/api/restricted_1.5.0-beta01.txt
+++ b/compose/animation/animation-graphics/api/restricted_1.5.0-beta01.txt
@@ -1,35 +1 @@
// Signature format: 4.0
-package androidx.compose.animation.graphics {
-
- @kotlin.RequiresOptIn(message="This is an experimental animation graphics API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface ExperimentalAnimationGraphicsApi {
- }
-
-}
-
-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(androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion, @DrawableRes int id);
- }
-
-}
-
-package androidx.compose.animation.graphics.vector {
-
- @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();
- 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 {
- }
-
-}
-
diff --git a/compose/animation/animation-tooling-internal/api/public_plus_experimental_1.5.0-beta01.txt b/compose/animation/animation-tooling-internal/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..50e70a5
--- /dev/null
+++ b/compose/animation/animation-tooling-internal/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,57 @@
+// Signature format: 4.0
+package androidx.compose.animation.tooling {
+
+ public final class ComposeAnimatedProperty {
+ ctor public ComposeAnimatedProperty(String label, Object value);
+ method public String component1();
+ method public Object component2();
+ method public androidx.compose.animation.tooling.ComposeAnimatedProperty copy(String label, Object value);
+ method public String getLabel();
+ method public Object getValue();
+ property public final String label;
+ property public final Object value;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ComposeAnimation {
+ method public Object getAnimationObject();
+ method public default String? getLabel();
+ method public default java.util.Set<java.lang.Object> getStates();
+ method public androidx.compose.animation.tooling.ComposeAnimationType getType();
+ property public abstract Object animationObject;
+ property public default String? label;
+ property public default java.util.Set<java.lang.Object> states;
+ property public abstract androidx.compose.animation.tooling.ComposeAnimationType type;
+ }
+
+ public enum ComposeAnimationType {
+ method public static androidx.compose.animation.tooling.ComposeAnimationType valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.animation.tooling.ComposeAnimationType[] values();
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATABLE;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATED_CONTENT;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATED_VALUE;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATED_VISIBILITY;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATE_CONTENT_SIZE;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType ANIMATE_X_AS_STATE;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType DECAY_ANIMATION;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType INFINITE_TRANSITION;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType TARGET_BASED_ANIMATION;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType TRANSITION_ANIMATION;
+ enum_constant public static final androidx.compose.animation.tooling.ComposeAnimationType UNSUPPORTED;
+ }
+
+ public final class TransitionInfo {
+ ctor public TransitionInfo(String label, String specType, long startTimeMillis, long endTimeMillis, java.util.Map<java.lang.Long,?> values);
+ method public long getEndTimeMillis();
+ method public String getLabel();
+ method public String getSpecType();
+ method public long getStartTimeMillis();
+ method public java.util.Map<java.lang.Long,java.lang.Object> getValues();
+ property public final long endTimeMillis;
+ property public final String label;
+ property public final String specType;
+ property public final long startTimeMillis;
+ property public final java.util.Map<java.lang.Long,java.lang.Object> values;
+ }
+
+}
+
diff --git a/compose/animation/animation/api/1.5.0-beta01.txt b/compose/animation/animation/api/1.5.0-beta01.txt
index 21baa95..f8122ac 100644
--- a/compose/animation/animation/api/1.5.0-beta01.txt
+++ b/compose/animation/animation/api/1.5.0-beta01.txt
@@ -10,7 +10,6 @@
method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(S targetState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional String label, optional kotlin.jvm.functions.Function1<? super S,?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
method public static androidx.compose.animation.SizeTransform SizeTransform(optional boolean clip, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.IntSize,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> sizeAnimationSpec);
method public static infix androidx.compose.animation.ContentTransform togetherWith(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
- method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi public static infix androidx.compose.animation.ContentTransform with(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
}
public sealed interface AnimatedContentScope extends androidx.compose.animation.AnimatedVisibilityScope {
@@ -43,19 +42,14 @@
public final class AnimatedVisibilityKt {
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, androidx.compose.animation.EnterTransition enter, androidx.compose.animation.ExitTransition exit, boolean initiallyVisible, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface AnimatedVisibilityScope {
- method @androidx.compose.animation.ExperimentalAnimationApi public default androidx.compose.ui.Modifier animateEnterExit(androidx.compose.ui.Modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label);
- method @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> getTransition();
- property @androidx.compose.animation.ExperimentalAnimationApi public abstract androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> transition;
}
public final class AnimationModifierKt {
@@ -80,19 +74,10 @@
}
public final class CrossfadeKt {
- method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void Crossfade(androidx.compose.animation.core.Transition<T>, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,?> contentKey, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional String label, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
method @Deprecated @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
- @androidx.compose.animation.ExperimentalAnimationApi public enum EnterExitState {
- method public static androidx.compose.animation.EnterExitState valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.animation.EnterExitState[] values();
- enum_constant public static final androidx.compose.animation.EnterExitState PostExit;
- enum_constant public static final androidx.compose.animation.EnterExitState PreEnter;
- enum_constant public static final androidx.compose.animation.EnterExitState Visible;
- }
-
public final class EnterExitTransitionKt {
method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Horizontal expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialWidth);
method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandIn(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntSize> initialSize);
@@ -132,9 +117,6 @@
property public final androidx.compose.animation.ExitTransition None;
}
- @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalAnimationApi {
- }
-
public final class SingleValueAnimationKt {
method public static androidx.compose.animation.core.Animatable<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D> Animatable(long initialValue);
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColorAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.graphics.Color> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,kotlin.Unit>? finishedListener);
diff --git a/compose/animation/animation/api/public_plus_experimental_1.5.0-beta01.txt b/compose/animation/animation/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..21baa95
--- /dev/null
+++ b/compose/animation/animation/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,176 @@
+// Signature format: 4.0
+package androidx.compose.animation {
+
+ public final class AndroidActualDefaultDecayAnimationSpec_androidKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> defaultDecayAnimationSpec();
+ }
+
+ public final class AnimatedContentKt {
+ method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(androidx.compose.animation.core.Transition<S>, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super S,?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(S targetState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional String label, optional kotlin.jvm.functions.Function1<? super S,?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
+ method public static androidx.compose.animation.SizeTransform SizeTransform(optional boolean clip, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.IntSize,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> sizeAnimationSpec);
+ method public static infix androidx.compose.animation.ContentTransform togetherWith(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
+ method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi public static infix androidx.compose.animation.ContentTransform with(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
+ }
+
+ public sealed interface AnimatedContentScope extends androidx.compose.animation.AnimatedVisibilityScope {
+ }
+
+ public sealed interface AnimatedContentTransitionScope<S> extends androidx.compose.animation.core.Transition.Segment<S> {
+ method public androidx.compose.animation.EnterTransition slideIntoContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffset);
+ method public androidx.compose.animation.ExitTransition slideOutOfContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffset);
+ method public infix androidx.compose.animation.ContentTransform using(androidx.compose.animation.ContentTransform, androidx.compose.animation.SizeTransform? sizeTransform);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public static final value class AnimatedContentTransitionScope.SlideDirection {
+ field public static final androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection.Companion Companion;
+ }
+
+ public static final class AnimatedContentTransitionScope.SlideDirection.Companion {
+ method public int getDown();
+ method public int getEnd();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public int getUp();
+ property public final int Down;
+ property public final int End;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ property public final int Up;
+ }
+
+ public final class AnimatedVisibilityKt {
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, androidx.compose.animation.EnterTransition enter, androidx.compose.animation.ExitTransition exit, boolean initiallyVisible, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface AnimatedVisibilityScope {
+ method @androidx.compose.animation.ExperimentalAnimationApi public default androidx.compose.ui.Modifier animateEnterExit(androidx.compose.ui.Modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label);
+ method @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> getTransition();
+ property @androidx.compose.animation.ExperimentalAnimationApi public abstract androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> transition;
+ }
+
+ public final class AnimationModifierKt {
+ method public static androidx.compose.ui.Modifier animateContentSize(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.IntSize,? super androidx.compose.ui.unit.IntSize,kotlin.Unit>? finishedListener);
+ }
+
+ public final class ColorVectorConverterKt {
+ method public static kotlin.jvm.functions.Function1<androidx.compose.ui.graphics.colorspace.ColorSpace,androidx.compose.animation.core.TwoWayConverter<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D>> getVectorConverter(androidx.compose.ui.graphics.Color.Companion);
+ }
+
+ public final class ContentTransform {
+ ctor public ContentTransform(androidx.compose.animation.EnterTransition targetContentEnter, androidx.compose.animation.ExitTransition initialContentExit, optional float targetContentZIndex, optional androidx.compose.animation.SizeTransform? sizeTransform);
+ method public androidx.compose.animation.ExitTransition getInitialContentExit();
+ method public androidx.compose.animation.SizeTransform? getSizeTransform();
+ method public androidx.compose.animation.EnterTransition getTargetContentEnter();
+ method public float getTargetContentZIndex();
+ method public void setTargetContentZIndex(float);
+ property public final androidx.compose.animation.ExitTransition initialContentExit;
+ property public final androidx.compose.animation.SizeTransform? sizeTransform;
+ property public final androidx.compose.animation.EnterTransition targetContentEnter;
+ property public final float targetContentZIndex;
+ }
+
+ public final class CrossfadeKt {
+ method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void Crossfade(androidx.compose.animation.core.Transition<T>, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,?> contentKey, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional String label, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ }
+
+ @androidx.compose.animation.ExperimentalAnimationApi public enum EnterExitState {
+ method public static androidx.compose.animation.EnterExitState valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.animation.EnterExitState[] values();
+ enum_constant public static final androidx.compose.animation.EnterExitState PostExit;
+ enum_constant public static final androidx.compose.animation.EnterExitState PreEnter;
+ enum_constant public static final androidx.compose.animation.EnterExitState Visible;
+ }
+
+ public final class EnterExitTransitionKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Horizontal expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialWidth);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandIn(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntSize> initialSize);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandVertically(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Vertical expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialHeight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition fadeIn(optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional float initialAlpha);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition fadeOut(optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional float targetAlpha);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition scaleIn(optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional float initialScale, optional long transformOrigin);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition scaleOut(optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional float targetScale, optional long transformOrigin);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition shrinkHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Horizontal shrinkTowards, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetWidth);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition shrinkOut(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment shrinkTowards, optional boolean clip, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntSize> targetSize);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition shrinkVertically(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Vertical shrinkTowards, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetHeight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition slideIn(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntOffset> initialOffset);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition slideInHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffsetX);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition slideInVertically(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffsetY);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition slideOut(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntOffset> targetOffset);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition slideOutHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffsetX);
+ method @androidx.compose.runtime.Stable public static androidx.compose.animation.ExitTransition slideOutVertically(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffsetY);
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class EnterTransition {
+ method @androidx.compose.runtime.Stable public final operator androidx.compose.animation.EnterTransition plus(androidx.compose.animation.EnterTransition enter);
+ field public static final androidx.compose.animation.EnterTransition.Companion Companion;
+ }
+
+ public static final class EnterTransition.Companion {
+ method public androidx.compose.animation.EnterTransition getNone();
+ property public final androidx.compose.animation.EnterTransition None;
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class ExitTransition {
+ method @androidx.compose.runtime.Stable public final operator androidx.compose.animation.ExitTransition plus(androidx.compose.animation.ExitTransition exit);
+ field public static final androidx.compose.animation.ExitTransition.Companion Companion;
+ }
+
+ public static final class ExitTransition.Companion {
+ method public androidx.compose.animation.ExitTransition getNone();
+ property public final androidx.compose.animation.ExitTransition None;
+ }
+
+ @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalAnimationApi {
+ }
+
+ public final class SingleValueAnimationKt {
+ method public static androidx.compose.animation.core.Animatable<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D> Animatable(long initialValue);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColorAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.graphics.Color> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,kotlin.Unit>? finishedListener);
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColorAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.graphics.Color> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,kotlin.Unit>? finishedListener);
+ }
+
+ public interface SizeTransform {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> createAnimationSpec(long initialSize, long targetSize);
+ method public boolean getClip();
+ property public abstract boolean clip;
+ }
+
+ public final class SplineBasedDecayKt {
+ method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> splineBasedDecay(androidx.compose.ui.unit.Density density);
+ }
+
+ public final class SplineBasedFloatDecayAnimationSpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
+ ctor public SplineBasedFloatDecayAnimationSpec(androidx.compose.ui.unit.Density density);
+ method public float getAbsVelocityThreshold();
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ property public float absVelocityThreshold;
+ }
+
+ public final class SplineBasedFloatDecayAnimationSpec_androidKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> rememberSplineBasedDecay();
+ method @Deprecated public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> splineBasedDecay(androidx.compose.ui.unit.Density density);
+ }
+
+ public final class TransitionKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.InfiniteTransition, long initialValue, long targetValue, androidx.compose.animation.core.InfiniteRepeatableSpec<androidx.compose.ui.graphics.Color> animationSpec);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.InfiniteTransition, long initialValue, long targetValue, androidx.compose.animation.core.InfiniteRepeatableSpec<androidx.compose.ui.graphics.Color> animationSpec, optional String label);
+ method @androidx.compose.runtime.Composable public static inline <S> androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColor(androidx.compose.animation.core.Transition<S>, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.core.Transition.Segment<S>,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.graphics.Color>> transitionSpec, optional String label, kotlin.jvm.functions.Function1<? super S,androidx.compose.ui.graphics.Color> targetValueByState);
+ }
+
+}
+
diff --git a/compose/animation/animation/api/restricted_1.5.0-beta01.txt b/compose/animation/animation/api/restricted_1.5.0-beta01.txt
index 21baa95..f8122ac 100644
--- a/compose/animation/animation/api/restricted_1.5.0-beta01.txt
+++ b/compose/animation/animation/api/restricted_1.5.0-beta01.txt
@@ -10,7 +10,6 @@
method @androidx.compose.runtime.Composable public static <S> void AnimatedContent(S targetState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<S>,androidx.compose.animation.ContentTransform> transitionSpec, optional androidx.compose.ui.Alignment contentAlignment, optional String label, optional kotlin.jvm.functions.Function1<? super S,?> contentKey, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super S,kotlin.Unit> content);
method public static androidx.compose.animation.SizeTransform SizeTransform(optional boolean clip, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.IntSize,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize>> sizeAnimationSpec);
method public static infix androidx.compose.animation.ContentTransform togetherWith(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
- method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi public static infix androidx.compose.animation.ContentTransform with(androidx.compose.animation.EnterTransition, androidx.compose.animation.ExitTransition exit);
}
public sealed interface AnimatedContentScope extends androidx.compose.animation.AnimatedVisibilityScope {
@@ -43,19 +42,14 @@
public final class AnimatedVisibilityKt {
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @Deprecated @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, androidx.compose.animation.EnterTransition enter, androidx.compose.animation.ExitTransition exit, boolean initiallyVisible, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface AnimatedVisibilityScope {
- method @androidx.compose.animation.ExperimentalAnimationApi public default androidx.compose.ui.Modifier animateEnterExit(androidx.compose.ui.Modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label);
- method @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> getTransition();
- property @androidx.compose.animation.ExperimentalAnimationApi public abstract androidx.compose.animation.core.Transition<androidx.compose.animation.EnterExitState> transition;
}
public final class AnimationModifierKt {
@@ -80,19 +74,10 @@
}
public final class CrossfadeKt {
- method @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void Crossfade(androidx.compose.animation.core.Transition<T>, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,?> contentKey, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, optional String label, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
method @Deprecated @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
- @androidx.compose.animation.ExperimentalAnimationApi public enum EnterExitState {
- method public static androidx.compose.animation.EnterExitState valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.animation.EnterExitState[] values();
- enum_constant public static final androidx.compose.animation.EnterExitState PostExit;
- enum_constant public static final androidx.compose.animation.EnterExitState PreEnter;
- enum_constant public static final androidx.compose.animation.EnterExitState Visible;
- }
-
public final class EnterExitTransitionKt {
method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandHorizontally(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment.Horizontal expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialWidth);
method @androidx.compose.runtime.Stable public static androidx.compose.animation.EnterTransition expandIn(optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> animationSpec, optional androidx.compose.ui.Alignment expandFrom, optional boolean clip, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,androidx.compose.ui.unit.IntSize> initialSize);
@@ -132,9 +117,6 @@
property public final androidx.compose.animation.ExitTransition None;
}
- @kotlin.RequiresOptIn(message="This is an experimental animation API.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalAnimationApi {
- }
-
public final class SingleValueAnimationKt {
method public static androidx.compose.animation.core.Animatable<androidx.compose.ui.graphics.Color,androidx.compose.animation.core.AnimationVector4D> Animatable(long initialValue);
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> animateColorAsState(long targetValue, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.graphics.Color> animationSpec, optional String label, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Color,kotlin.Unit>? finishedListener);
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index dac5856..6305200 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -127,7 +127,7 @@
* The maven version string of this compiler. This string should be updated before/after every
* release.
*/
- const val compilerVersion: String = "1.5.0-alpha05"
+ const val compilerVersion: String = "1.4.7"
private val minimumRuntimeVersion: String
get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
}
diff --git a/compose/foundation/foundation-layout/api/1.5.0-beta01.txt b/compose/foundation/foundation-layout/api/1.5.0-beta01.txt
index ec4aa19d..147caf6 100644
--- a/compose/foundation/foundation-layout/api/1.5.0-beta01.txt
+++ b/compose/foundation/foundation-layout/api/1.5.0-beta01.txt
@@ -111,20 +111,6 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
}
- @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
- }
-
- @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- }
-
- public final class FlowLayoutKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
- }
-
- @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
- }
-
public final class IntrinsicKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
@@ -142,17 +128,6 @@
@kotlin.DslMarker public @interface LayoutScopeMarker {
}
- @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class MutableWindowInsets implements androidx.compose.foundation.layout.WindowInsets {
- ctor public MutableWindowInsets(optional androidx.compose.foundation.layout.WindowInsets initialInsets);
- method public int getBottom(androidx.compose.ui.unit.Density density);
- method public androidx.compose.foundation.layout.WindowInsets getInsets();
- method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
- method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
- method public int getTop(androidx.compose.ui.unit.Density density);
- method public void setInsets(androidx.compose.foundation.layout.WindowInsets);
- property public final androidx.compose.foundation.layout.WindowInsets insets;
- }
-
public final class OffsetKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional float x, optional float y);
method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
@@ -241,10 +216,6 @@
public static final class WindowInsets.Companion {
}
- public final class WindowInsetsConnection_androidKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi public static androidx.compose.ui.Modifier imeNestedScroll(androidx.compose.ui.Modifier);
- }
-
public final class WindowInsetsKt {
method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
@@ -257,13 +228,9 @@
}
public final class WindowInsetsPaddingKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onConsumedWindowInsetsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier withConsumedWindowInsets(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
}
public final class WindowInsetsPadding_androidKt {
@@ -313,33 +280,20 @@
}
public final class WindowInsets_androidKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreNavigationBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreStatusBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreSystemBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBarIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationSource(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationTarget(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElementIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isCaptionBarVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isImeVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isTappableElementVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
method public static void setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean);
}
diff --git a/compose/foundation/foundation-layout/api/public_plus_experimental_1.5.0-beta01.txt b/compose/foundation/foundation-layout/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..ec4aa19d
--- /dev/null
+++ b/compose/foundation/foundation-layout/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,347 @@
+// Signature format: 4.0
+package androidx.compose.foundation.layout {
+
+ public final class AlignmentLineKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier paddingFrom(androidx.compose.ui.Modifier, androidx.compose.ui.layout.AlignmentLine alignmentLine, optional float before, optional float after);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier paddingFrom(androidx.compose.ui.Modifier, androidx.compose.ui.layout.AlignmentLine alignmentLine, optional long before, optional long after);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier paddingFromBaseline(androidx.compose.ui.Modifier, optional float top, optional float bottom);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier paddingFromBaseline(androidx.compose.ui.Modifier, optional long top, optional long bottom);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Arrangement {
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Horizontal aligned(androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Vertical aligned(androidx.compose.ui.Alignment.Vertical alignment);
+ method public androidx.compose.foundation.layout.Arrangement.Vertical getBottom();
+ method public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical getCenter();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getEnd();
+ method public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical getSpaceAround();
+ method public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical getSpaceBetween();
+ method public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical getSpaceEvenly();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getStart();
+ method public androidx.compose.foundation.layout.Arrangement.Vertical getTop();
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical spacedBy(float space);
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Horizontal spacedBy(float space, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Vertical spacedBy(float space, androidx.compose.ui.Alignment.Vertical alignment);
+ property public final androidx.compose.foundation.layout.Arrangement.Vertical Bottom;
+ property public final androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical Center;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal End;
+ property public final androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical SpaceAround;
+ property public final androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical SpaceBetween;
+ property public final androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical SpaceEvenly;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal Start;
+ property public final androidx.compose.foundation.layout.Arrangement.Vertical Top;
+ field public static final androidx.compose.foundation.layout.Arrangement INSTANCE;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class Arrangement.Absolute {
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Horizontal aligned(androidx.compose.ui.Alignment.Horizontal alignment);
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getCenter();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getLeft();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getRight();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getSpaceAround();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getSpaceBetween();
+ method public androidx.compose.foundation.layout.Arrangement.Horizontal getSpaceEvenly();
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical spacedBy(float space);
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Horizontal spacedBy(float space, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.foundation.layout.Arrangement.Vertical spacedBy(float space, androidx.compose.ui.Alignment.Vertical alignment);
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal Center;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal Left;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal Right;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal SpaceAround;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal SpaceBetween;
+ property public final androidx.compose.foundation.layout.Arrangement.Horizontal SpaceEvenly;
+ field public static final androidx.compose.foundation.layout.Arrangement.Absolute INSTANCE;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public static interface Arrangement.Horizontal {
+ method public void arrange(androidx.compose.ui.unit.Density, int totalSize, int[] sizes, androidx.compose.ui.unit.LayoutDirection layoutDirection, int[] outPositions);
+ method public default float getSpacing();
+ property public default float spacing;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public static interface Arrangement.HorizontalOrVertical extends androidx.compose.foundation.layout.Arrangement.Horizontal androidx.compose.foundation.layout.Arrangement.Vertical {
+ property public default float spacing;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public static interface Arrangement.Vertical {
+ method public void arrange(androidx.compose.ui.unit.Density, int totalSize, int[] sizes, int[] outPositions);
+ method public default float getSpacing();
+ property public default float spacing;
+ }
+
+ public final class AspectRatioKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, float ratio, optional boolean matchHeightConstraintsFirst);
+ }
+
+ public final class BoxKt {
+ method @androidx.compose.runtime.Composable public static void Box(androidx.compose.ui.Modifier modifier);
+ method @androidx.compose.runtime.Composable public static inline void Box(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional boolean propagateMinConstraints, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface BoxScope {
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier matchParentSize(androidx.compose.ui.Modifier);
+ }
+
+ public final class BoxWithConstraintsKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void BoxWithConstraints(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional boolean propagateMinConstraints, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxWithConstraintsScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.runtime.Stable public interface BoxWithConstraintsScope extends androidx.compose.foundation.layout.BoxScope {
+ method public long getConstraints();
+ method public float getMaxHeight();
+ method public float getMaxWidth();
+ method public float getMinHeight();
+ method public float getMinWidth();
+ property public abstract long constraints;
+ property public abstract float maxHeight;
+ property public abstract float maxWidth;
+ property public abstract float minHeight;
+ property public abstract float minWidth;
+ }
+
+ public final class ColumnKt {
+ method @androidx.compose.runtime.Composable public static inline void Column(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface ColumnScope {
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.VerticalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+ }
+
+ @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
+ }
+
+ @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ }
+
+ public final class FlowLayoutKt {
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ }
+
+ public final class IntrinsicKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
+ }
+
+ public enum IntrinsicSize {
+ method public static androidx.compose.foundation.layout.IntrinsicSize valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.foundation.layout.IntrinsicSize[] values();
+ enum_constant public static final androidx.compose.foundation.layout.IntrinsicSize Max;
+ enum_constant public static final androidx.compose.foundation.layout.IntrinsicSize Min;
+ }
+
+ @kotlin.DslMarker public @interface LayoutScopeMarker {
+ }
+
+ @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class MutableWindowInsets implements androidx.compose.foundation.layout.WindowInsets {
+ ctor public MutableWindowInsets(optional androidx.compose.foundation.layout.WindowInsets initialInsets);
+ method public int getBottom(androidx.compose.ui.unit.Density density);
+ method public androidx.compose.foundation.layout.WindowInsets getInsets();
+ method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public int getTop(androidx.compose.ui.unit.Density density);
+ method public void setInsets(androidx.compose.foundation.layout.WindowInsets);
+ property public final androidx.compose.foundation.layout.WindowInsets insets;
+ }
+
+ public final class OffsetKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional float x, optional float y);
+ method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, optional float x, optional float y);
+ method public static androidx.compose.ui.Modifier offset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
+ }
+
+ public final class PaddingKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.layout.PaddingValues PaddingValues(float all);
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.layout.PaddingValues PaddingValues(optional float horizontal, optional float vertical);
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.layout.PaddingValues PaddingValues(optional float start, optional float top, optional float end, optional float bottom);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absolutePadding(androidx.compose.ui.Modifier, optional float left, optional float top, optional float right, optional float bottom);
+ method @androidx.compose.runtime.Stable public static float calculateEndPadding(androidx.compose.foundation.layout.PaddingValues, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method @androidx.compose.runtime.Stable public static float calculateStartPadding(androidx.compose.foundation.layout.PaddingValues, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, float all);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, optional float horizontal, optional float vertical);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier padding(androidx.compose.ui.Modifier, optional float start, optional float top, optional float end, optional float bottom);
+ }
+
+ @androidx.compose.runtime.Stable public interface PaddingValues {
+ method public float calculateBottomPadding();
+ method public float calculateLeftPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public float calculateRightPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public float calculateTopPadding();
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PaddingValues.Absolute implements androidx.compose.foundation.layout.PaddingValues {
+ ctor public PaddingValues.Absolute(optional @androidx.compose.runtime.Stable float left, optional @androidx.compose.runtime.Stable float top, optional @androidx.compose.runtime.Stable float right, optional @androidx.compose.runtime.Stable float bottom);
+ method public float calculateBottomPadding();
+ method public float calculateLeftPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public float calculateRightPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public float calculateTopPadding();
+ }
+
+ public final class RowKt {
+ method @androidx.compose.runtime.Composable public static inline void Row(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface RowScope {
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Vertical alignment);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.HorizontalAlignmentLine alignmentLine);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignByBaseline(androidx.compose.ui.Modifier);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+ }
+
+ public final class SizeKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSize(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional float fraction);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, float height);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn(androidx.compose.ui.Modifier, optional float min, optional float max);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, float height);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeightIn(androidx.compose.ui.Modifier, optional float min, optional float max);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredSize(androidx.compose.ui.Modifier, float size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredSize(androidx.compose.ui.Modifier, float width, float height);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredSize(androidx.compose.ui.Modifier, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredSizeIn(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredWidth(androidx.compose.ui.Modifier, float width);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredWidthIn(androidx.compose.ui.Modifier, optional float min, optional float max);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size(androidx.compose.ui.Modifier, float size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size(androidx.compose.ui.Modifier, float width, float height);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier size(androidx.compose.ui.Modifier, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier sizeIn(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight, optional float maxWidth, optional float maxHeight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier width(androidx.compose.ui.Modifier, float width);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier widthIn(androidx.compose.ui.Modifier, optional float min, optional float max);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentHeight(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Vertical align, optional boolean unbounded);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentSize(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment align, optional boolean unbounded);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier wrapContentWidth(androidx.compose.ui.Modifier, optional androidx.compose.ui.Alignment.Horizontal align, optional boolean unbounded);
+ }
+
+ public final class SpacerKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Spacer(androidx.compose.ui.Modifier modifier);
+ }
+
+ @androidx.compose.runtime.Stable public interface WindowInsets {
+ method public int getBottom(androidx.compose.ui.unit.Density density);
+ method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public int getTop(androidx.compose.ui.unit.Density density);
+ field public static final androidx.compose.foundation.layout.WindowInsets.Companion Companion;
+ }
+
+ public static final class WindowInsets.Companion {
+ }
+
+ public final class WindowInsetsConnection_androidKt {
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi public static androidx.compose.ui.Modifier imeNestedScroll(androidx.compose.ui.Modifier);
+ }
+
+ public final class WindowInsetsKt {
+ method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
+ method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
+ method public static androidx.compose.foundation.layout.WindowInsets add(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.foundation.layout.PaddingValues asPaddingValues(androidx.compose.foundation.layout.WindowInsets);
+ method public static androidx.compose.foundation.layout.PaddingValues asPaddingValues(androidx.compose.foundation.layout.WindowInsets, androidx.compose.ui.unit.Density density);
+ method public static androidx.compose.foundation.layout.WindowInsets exclude(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+ method public static androidx.compose.foundation.layout.WindowInsets only(androidx.compose.foundation.layout.WindowInsets, int sides);
+ method public static androidx.compose.foundation.layout.WindowInsets union(androidx.compose.foundation.layout.WindowInsets, androidx.compose.foundation.layout.WindowInsets insets);
+ }
+
+ public final class WindowInsetsPaddingKt {
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
+ method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onConsumedWindowInsetsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier withConsumedWindowInsets(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
+ }
+
+ public final class WindowInsetsPadding_androidKt {
+ method public static androidx.compose.ui.Modifier captionBarPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier displayCutoutPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier imePadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier mandatorySystemGesturesPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier navigationBarsPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier safeContentPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier safeDrawingPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier safeGesturesPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier statusBarsPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier systemBarsPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier systemGesturesPadding(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier waterfallPadding(androidx.compose.ui.Modifier);
+ }
+
+ @kotlin.jvm.JvmInline public final value class WindowInsetsSides {
+ method public operator int plus(int sides);
+ field public static final androidx.compose.foundation.layout.WindowInsetsSides.Companion Companion;
+ }
+
+ public static final class WindowInsetsSides.Companion {
+ method public int getBottom();
+ method public int getEnd();
+ method public int getHorizontal();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public int getTop();
+ method public int getVertical();
+ property public final int Bottom;
+ property public final int End;
+ property public final int Horizontal;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ property public final int Top;
+ property public final int Vertical;
+ }
+
+ public final class WindowInsetsSizeKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsBottomHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsEndWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsStartWidth(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsTopHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
+ }
+
+ public final class WindowInsets_androidKt {
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreNavigationBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreStatusBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreSystemBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBarIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationSource(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationTarget(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElementIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isCaptionBarVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isImeVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isTappableElementVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
+ method public static void setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean);
+ }
+
+}
+
diff --git a/compose/foundation/foundation-layout/api/restricted_1.5.0-beta01.txt b/compose/foundation/foundation-layout/api/restricted_1.5.0-beta01.txt
index 6fe644c..ee1efa2 100644
--- a/compose/foundation/foundation-layout/api/restricted_1.5.0-beta01.txt
+++ b/compose/foundation/foundation-layout/api/restricted_1.5.0-beta01.txt
@@ -114,22 +114,11 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
}
- @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
- }
-
- @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- }
-
public final class FlowLayoutKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static inline void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy columnMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, int maxItemsInMainAxis);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy rowMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, int maxItemsInMainAxis);
}
- @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
- }
-
public final class IntrinsicKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.IntrinsicSize intrinsicSize);
@@ -147,17 +136,6 @@
@kotlin.DslMarker public @interface LayoutScopeMarker {
}
- @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class MutableWindowInsets implements androidx.compose.foundation.layout.WindowInsets {
- ctor public MutableWindowInsets(optional androidx.compose.foundation.layout.WindowInsets initialInsets);
- method public int getBottom(androidx.compose.ui.unit.Density density);
- method public androidx.compose.foundation.layout.WindowInsets getInsets();
- method public int getLeft(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
- method public int getRight(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection);
- method public int getTop(androidx.compose.ui.unit.Density density);
- method public void setInsets(androidx.compose.foundation.layout.WindowInsets);
- property public final androidx.compose.foundation.layout.WindowInsets insets;
- }
-
public final class OffsetKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, optional float x, optional float y);
method public static androidx.compose.ui.Modifier absoluteOffset(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.unit.IntOffset> offset);
@@ -248,10 +226,6 @@
public static final class WindowInsets.Companion {
}
- public final class WindowInsetsConnection_androidKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi public static androidx.compose.ui.Modifier imeNestedScroll(androidx.compose.ui.Modifier);
- }
-
public final class WindowInsetsKt {
method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional float left, optional float top, optional float right, optional float bottom);
method public static androidx.compose.foundation.layout.WindowInsets WindowInsets(optional int left, optional int top, optional int right, optional int bottom);
@@ -264,13 +238,9 @@
}
public final class WindowInsetsPaddingKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumeWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.PaddingValues paddingValues);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier consumedWindowInsets(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onConsumedWindowInsetsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier windowInsetsPadding(androidx.compose.ui.Modifier, androidx.compose.foundation.layout.WindowInsets insets);
- method @Deprecated @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier withConsumedWindowInsets(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.WindowInsets,kotlin.Unit> block);
}
public final class WindowInsetsPadding_androidKt {
@@ -320,33 +290,20 @@
}
public final class WindowInsets_androidKt {
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreNavigationBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreStatusBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreSystemBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBarIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationSource(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationTarget(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getMandatorySystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getNavigationBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeContent(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeDrawing(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSafeGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getStatusBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBars(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemBarsIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getSystemGestures(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElement(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getTappableElementIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getWaterfall(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isCaptionBarVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isImeVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
- method @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isTappableElementVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
method public static void setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean);
}
diff --git a/compose/foundation/foundation/api/1.5.0-beta01.txt b/compose/foundation/foundation/api/1.5.0-beta01.txt
index 15fd553..8be51e4 100644
--- a/compose/foundation/foundation/api/1.5.0-beta01.txt
+++ b/compose/foundation/foundation/api/1.5.0-beta01.txt
@@ -6,19 +6,6 @@
method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, long color, optional androidx.compose.ui.graphics.Shape shape);
}
- public final class BasicMarqueeKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing MarqueeSpacing(float spacing);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier basicMarquee(androidx.compose.ui.Modifier, optional int iterations, optional int animationMode, optional int delayMillis, optional int initialDelayMillis, optional androidx.compose.foundation.MarqueeSpacing spacing, optional float velocity);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeDelayMillis();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeIterations();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing getDefaultMarqueeSpacing();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static float getDefaultMarqueeVelocity();
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeDelayMillis;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeIterations;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.foundation.MarqueeSpacing DefaultMarqueeSpacing;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final float DefaultMarqueeVelocity;
- }
-
public final class BorderKt {
method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, androidx.compose.foundation.BorderStroke border, optional androidx.compose.ui.graphics.Shape shape);
method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, float width, androidx.compose.ui.graphics.Brush brush, androidx.compose.ui.graphics.Shape shape);
@@ -39,7 +26,6 @@
}
public final class CanvasKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, String contentDescription, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
method @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
}
@@ -50,8 +36,6 @@
public final class ClickableKt {
method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
}
public final class ClipScrollableContainerKt {
@@ -67,18 +51,10 @@
method @Deprecated public static androidx.compose.ui.Modifier excludeFromSystemGesture(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
}
- @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalFoundationApi {
- }
-
public final class FocusableKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier focusGroup(androidx.compose.ui.Modifier);
method public static androidx.compose.ui.Modifier focusable(androidx.compose.ui.Modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
- public final class FocusedBoundsKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier onFocusedBoundsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,kotlin.Unit> onPositioned);
- }
-
public final class HoverableKt {
method public static androidx.compose.ui.Modifier hoverable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional boolean enabled);
}
@@ -104,47 +80,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.Indication> LocalIndication;
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationApi {
- }
-
- public final class MagnifierKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier magnifier(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> sourceCenter, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> magnifierCenter, optional float zoom, optional androidx.compose.foundation.MagnifierStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.DpSize,kotlin.Unit>? onSizeChanged);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class MagnifierStyle {
- ctor @androidx.compose.foundation.ExperimentalFoundationApi public MagnifierStyle(optional long size, optional float cornerRadius, optional float elevation, optional boolean clippingEnabled, optional boolean fishEyeEnabled);
- method public boolean isSupported();
- property public final boolean isSupported;
- field public static final androidx.compose.foundation.MagnifierStyle.Companion Companion;
- }
-
- public static final class MagnifierStyle.Companion {
- method public androidx.compose.foundation.MagnifierStyle getDefault();
- method public androidx.compose.foundation.MagnifierStyle getTextDefault();
- property public final androidx.compose.foundation.MagnifierStyle Default;
- property public final androidx.compose.foundation.MagnifierStyle TextDefault;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @kotlin.jvm.JvmInline public final value class MarqueeAnimationMode {
- field public static final androidx.compose.foundation.MarqueeAnimationMode.Companion Companion;
- }
-
- public static final class MarqueeAnimationMode.Companion {
- method @androidx.compose.foundation.ExperimentalFoundationApi public int getImmediately();
- method @androidx.compose.foundation.ExperimentalFoundationApi public int getWhileFocused();
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int Immediately;
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int WhileFocused;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface MarqueeSpacing {
- method @androidx.compose.foundation.ExperimentalFoundationApi public int calculateSpacing(androidx.compose.ui.unit.Density, int contentWidth, int containerWidth);
- field public static final androidx.compose.foundation.MarqueeSpacing.Companion Companion;
- }
-
- public static final class MarqueeSpacing.Companion {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.foundation.MarqueeSpacing fractionOfContainer(float fraction);
- }
-
public enum MutatePriority {
method public static androidx.compose.foundation.MutatePriority valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
method public static androidx.compose.foundation.MutatePriority[] values();
@@ -159,32 +94,6 @@
method public suspend <T, R> Object? mutateWith(T receiver, optional androidx.compose.foundation.MutatePriority priority, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
}
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class OverscrollConfiguration {
- ctor public OverscrollConfiguration(optional long glowColor, optional androidx.compose.foundation.layout.PaddingValues drawPadding);
- method public androidx.compose.foundation.layout.PaddingValues getDrawPadding();
- method public long getGlowColor();
- property public final androidx.compose.foundation.layout.PaddingValues drawPadding;
- property public final long glowColor;
- }
-
- public final class OverscrollConfigurationKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> getLocalOverscrollConfiguration();
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> LocalOverscrollConfiguration;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface OverscrollEffect {
- method public suspend Object? applyToFling(long velocity, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Velocity,? super kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>,?> performFling, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public long applyToScroll(long delta, int source, kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset> performScroll);
- method public androidx.compose.ui.Modifier getEffectModifier();
- method public boolean isInProgress();
- property public abstract androidx.compose.ui.Modifier effectModifier;
- property public abstract boolean isInProgress;
- }
-
- public final class OverscrollKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier overscroll(androidx.compose.ui.Modifier, androidx.compose.foundation.OverscrollEffect overscrollEffect);
- }
-
public final class ProgressSemanticsKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps);
@@ -298,13 +207,11 @@
public final class ScrollableDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior();
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public androidx.compose.foundation.OverscrollEffect overscrollEffect();
method public boolean reverseDirection(androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.foundation.gestures.Orientation orientation, boolean reverseScrolling);
field public static final androidx.compose.foundation.gestures.ScrollableDefaults INSTANCE;
}
public final class ScrollableKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, androidx.compose.foundation.OverscrollEffect? overscrollEffect, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
@@ -347,7 +254,6 @@
public final class TransformableKt {
method public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, optional boolean lockRotationOnZoomPan, optional boolean enabled);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, kotlin.jvm.functions.Function0<java.lang.Boolean> canPan, optional boolean lockRotationOnZoomPan, optional boolean enabled);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface TransformableState {
@@ -370,45 +276,6 @@
}
-package androidx.compose.foundation.gestures.snapping {
-
- public final class LazyGridSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
- }
-
- public final class LazyListSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
- ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onSettlingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
- }
-
- public final class SnapFlingBehaviorKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
- method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
- method public float calculateSnapStepSize(androidx.compose.ui.unit.Density);
- method public float calculateSnappingOffset(androidx.compose.ui.unit.Density, float currentVelocity);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
- method public int position(androidx.compose.ui.unit.Density, int layoutSize, int itemSize, int itemIndex);
- field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
- }
-
- public static final class SnapPositionInLayout.Companion {
- method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
- property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
- }
-
-}
-
package androidx.compose.foundation.interaction {
public interface DragInteraction extends androidx.compose.foundation.interaction.Interaction {
@@ -530,7 +397,6 @@
}
@androidx.compose.foundation.lazy.LazyScopeMarker @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
@@ -577,7 +443,6 @@
method @Deprecated public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public default void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
method @Deprecated 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, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
}
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
@@ -638,7 +503,6 @@
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
method public int getCurrentLineSpan();
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int currentLineSpan;
}
public final class LazyGridDslKt {
@@ -676,7 +540,6 @@
}
@androidx.compose.foundation.lazy.grid.LazyGridScopeMarker @androidx.compose.runtime.Stable public sealed interface LazyGridItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
}
@androidx.compose.foundation.lazy.grid.LazyGridScopeMarker public sealed interface LazyGridItemSpanScope {
@@ -753,110 +616,6 @@
}
-package androidx.compose.foundation.lazy.layout {
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface IntervalList<T> {
- method public void forEach(optional int fromIndex, optional int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
- method public operator androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
- method public int getSize();
- property public abstract int size;
- }
-
- public static final class IntervalList.Interval<T> {
- method public int getSize();
- method public int getStartIndex();
- method public T getValue();
- property public final int size;
- property public final int startIndex;
- property public final T value;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public abstract class LazyLayoutIntervalContent<Interval extends androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.Interval> {
- ctor public LazyLayoutIntervalContent();
- method public final Object? getContentType(int index);
- method public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> getIntervals();
- method public final int getItemCount();
- method public final Object getKey(int index);
- method public final inline <T> T withInterval(int globalIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super Interval,? extends T> block);
- property public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> intervals;
- property public final int itemCount;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static interface LazyLayoutIntervalContent.Interval {
- method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? getKey();
- method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> getType();
- property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? key;
- property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> type;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface LazyLayoutItemProvider {
- method @androidx.compose.runtime.Composable public void Item(int index, Object key);
- method public default Object? getContentType(int index);
- method public default int getIndex(Object key);
- method public int getItemCount();
- method public default Object getKey(int index);
- property public abstract int itemCount;
- }
-
- public final class LazyLayoutItemProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
- }
-
- public final class LazyLayoutKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface LazyLayoutMeasureScope extends androidx.compose.ui.layout.MeasureScope {
- method public java.util.List<androidx.compose.ui.layout.Placeable> measure(int index, long constraints);
- method @androidx.compose.runtime.Stable public default float toDp(float);
- method @androidx.compose.runtime.Stable public default float toDp(int);
- method @androidx.compose.runtime.Stable public default float toDp(long);
- method @androidx.compose.runtime.Stable public default long toDpSize(long);
- method @androidx.compose.runtime.Stable public default long toSize(long);
- method @androidx.compose.runtime.Stable public default long toSp(float);
- method @androidx.compose.runtime.Stable public default long toSp(float);
- method @androidx.compose.runtime.Stable public default long toSp(int);
- }
-
- public final class LazyLayoutPinnableItemKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayoutPinnableItem(Object? key, int index, androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList pinnedItemList, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class LazyLayoutPinnedItemList implements kotlin.jvm.internal.markers.KMappedMarker java.util.List<androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList.PinnedItem> {
- ctor public LazyLayoutPinnedItemList();
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static sealed interface LazyLayoutPinnedItemList.PinnedItem {
- method public int getIndex();
- method public Object? getKey();
- property public abstract int index;
- property public abstract Object? key;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class LazyLayoutPrefetchState {
- ctor public LazyLayoutPrefetchState();
- method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index, long constraints);
- }
-
- public static sealed interface LazyLayoutPrefetchState.PrefetchHandle {
- method public void cancel();
- }
-
- public final class Lazy_androidKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class MutableIntervalList<T> implements androidx.compose.foundation.lazy.layout.IntervalList<T> {
- ctor public MutableIntervalList();
- method public void addInterval(int size, T value);
- method public void forEach(int fromIndex, int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
- method public androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
- method public int getSize();
- property public int size;
- }
-
-}
-
package androidx.compose.foundation.lazy.staggeredgrid {
public final class LazyStaggeredGridDslKt {
@@ -884,7 +643,6 @@
}
@androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
}
public sealed interface LazyStaggeredGridLayoutInfo {
@@ -975,109 +733,6 @@
}
-package androidx.compose.foundation.pager {
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PageSize {
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fill implements androidx.compose.foundation.pager.PageSize {
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- field public static final androidx.compose.foundation.pager.PageSize.Fill INSTANCE;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fixed implements androidx.compose.foundation.pager.PageSize {
- ctor public PageSize.Fixed(float pageSize);
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- method public float getPageSize();
- property public final float pageSize;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class PagerDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional float snapVelocityThreshold, optional float snapPositionalThreshold);
- method public androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection(androidx.compose.foundation.gestures.Orientation orientation);
- field public static final androidx.compose.foundation.pager.PagerDefaults INSTANCE;
- }
-
- public final class PagerKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PagerSnapDistance {
- method public int calculateTargetPage(int startPage, int suggestedTargetPage, float velocity, int pageSize, int pageSpacing);
- field public static final androidx.compose.foundation.pager.PagerSnapDistance.Companion Companion;
- }
-
- public static final class PagerSnapDistance.Companion {
- method public androidx.compose.foundation.pager.PagerSnapDistance atMost(int pages);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public abstract class PagerState implements androidx.compose.foundation.gestures.ScrollableState {
- ctor public PagerState(optional int initialPage, optional float initialPageOffsetFraction);
- method public final suspend Object? animateScrollToPage(int page, optional float pageOffsetFraction, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public float dispatchRawDelta(float delta);
- method public final boolean getCanScrollBackward();
- method public final boolean getCanScrollForward();
- method public final int getCurrentPage();
- method public final float getCurrentPageOffsetFraction();
- method public final int getInitialPage();
- method public final float getInitialPageOffsetFraction();
- method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
- method public final float getOffsetFractionForPage(int page);
- method public abstract int getPageCount();
- method public final int getSettledPage();
- method public final int getTargetPage();
- method public boolean isScrollInProgress();
- method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public final suspend Object? scrollToPage(int page, optional float pageOffsetFraction, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean canScrollBackward;
- property public final boolean canScrollForward;
- property public final int currentPage;
- property public final float currentPageOffsetFraction;
- property public final int initialPage;
- property public final float initialPageOffsetFraction;
- property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
- property public boolean isScrollInProgress;
- property public abstract int pageCount;
- property public final int settledPage;
- property public final int targetPage;
- }
-
- public final class PagerStateKt {
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction, kotlin.jvm.functions.Function0<java.lang.Integer> pageCount);
- }
-
-}
-
-package androidx.compose.foundation.relocation {
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
- method public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class BringIntoViewRequesterKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.relocation.BringIntoViewRequester BringIntoViewRequester();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewRequester(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewRequester bringIntoViewRequester);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public interface BringIntoViewResponder {
- method @androidx.compose.foundation.ExperimentalFoundationApi public suspend Object? bringChildIntoView(kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Rect> localRect, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
- }
-
- public final class BringIntoViewResponderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewResponder(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewResponder responder);
- }
-
-}
-
package androidx.compose.foundation.selection {
public final class SelectableGroupKt {
@@ -1221,7 +876,6 @@
public final class ClickableTextKt {
method @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onHover, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
}
@androidx.compose.runtime.Immutable public final class InlineTextContent {
@@ -1236,9 +890,6 @@
method public static void appendInlineContent(androidx.compose.ui.text.AnnotatedString.Builder, String id, optional String alternateText);
}
- @kotlin.RequiresOptIn(message="Internal/Unstable API for use only between foundation modules sharing " + "the same exact version, subject to change without notice.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationTextApi {
- }
-
public interface KeyboardActionScope {
method public void defaultKeyboardAction(int imeAction);
}
diff --git a/compose/foundation/foundation/api/public_plus_experimental_1.5.0-beta01.txt b/compose/foundation/foundation/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..15fd553
--- /dev/null
+++ b/compose/foundation/foundation/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,1314 @@
+// Signature format: 4.0
+package androidx.compose.foundation {
+
+ public final class BackgroundKt {
+ method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Brush brush, optional androidx.compose.ui.graphics.Shape shape, optional float alpha);
+ method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, long color, optional androidx.compose.ui.graphics.Shape shape);
+ }
+
+ public final class BasicMarqueeKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing MarqueeSpacing(float spacing);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier basicMarquee(androidx.compose.ui.Modifier, optional int iterations, optional int animationMode, optional int delayMillis, optional int initialDelayMillis, optional androidx.compose.foundation.MarqueeSpacing spacing, optional float velocity);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeDelayMillis();
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeIterations();
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing getDefaultMarqueeSpacing();
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static float getDefaultMarqueeVelocity();
+ property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeDelayMillis;
+ property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeIterations;
+ property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.foundation.MarqueeSpacing DefaultMarqueeSpacing;
+ property @androidx.compose.foundation.ExperimentalFoundationApi public static final float DefaultMarqueeVelocity;
+ }
+
+ public final class BorderKt {
+ method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, androidx.compose.foundation.BorderStroke border, optional androidx.compose.ui.graphics.Shape shape);
+ method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, float width, androidx.compose.ui.graphics.Brush brush, androidx.compose.ui.graphics.Shape shape);
+ method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, float width, long color, optional androidx.compose.ui.graphics.Shape shape);
+ }
+
+ @androidx.compose.runtime.Immutable public final class BorderStroke {
+ ctor public BorderStroke(float width, androidx.compose.ui.graphics.Brush brush);
+ method public androidx.compose.foundation.BorderStroke copy(optional float width, optional androidx.compose.ui.graphics.Brush brush);
+ method public androidx.compose.ui.graphics.Brush getBrush();
+ method public float getWidth();
+ property public final androidx.compose.ui.graphics.Brush brush;
+ property public final float width;
+ }
+
+ public final class BorderStrokeKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.BorderStroke BorderStroke(float width, long color);
+ }
+
+ public final class CanvasKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, String contentDescription, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
+ method @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
+ }
+
+ public final class CheckScrollableContainerConstraintsKt {
+ method public static void checkScrollableContainerConstraints(long constraints, androidx.compose.foundation.gestures.Orientation orientation);
+ }
+
+ public final class ClickableKt {
+ method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ }
+
+ public final class ClipScrollableContainerKt {
+ method public static androidx.compose.ui.Modifier clipScrollableContainer(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.Orientation orientation);
+ }
+
+ public final class DarkThemeKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static boolean isSystemInDarkTheme();
+ }
+
+ public final class ExcludeFromSystemGesture_androidKt {
+ method @Deprecated public static androidx.compose.ui.Modifier excludeFromSystemGesture(androidx.compose.ui.Modifier);
+ method @Deprecated public static androidx.compose.ui.Modifier excludeFromSystemGesture(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
+ }
+
+ @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalFoundationApi {
+ }
+
+ public final class FocusableKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier focusGroup(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier focusable(androidx.compose.ui.Modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+ }
+
+ public final class FocusedBoundsKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier onFocusedBoundsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,kotlin.Unit> onPositioned);
+ }
+
+ public final class HoverableKt {
+ method public static androidx.compose.ui.Modifier hoverable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional boolean enabled);
+ }
+
+ public final class ImageKt {
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int filterQuality);
+ method @androidx.compose.runtime.Composable public static void Image(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Image(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ }
+
+ @androidx.compose.runtime.Stable public interface Indication {
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.IndicationInstance rememberUpdatedInstance(androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ }
+
+ public interface IndicationInstance {
+ method public void drawIndication(androidx.compose.ui.graphics.drawscope.ContentDrawScope);
+ }
+
+ public final class IndicationKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.Indication> getLocalIndication();
+ method public static androidx.compose.ui.Modifier indication(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.foundation.Indication? indication);
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.Indication> LocalIndication;
+ }
+
+ @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationApi {
+ }
+
+ public final class MagnifierKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier magnifier(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> sourceCenter, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> magnifierCenter, optional float zoom, optional androidx.compose.foundation.MagnifierStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.DpSize,kotlin.Unit>? onSizeChanged);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class MagnifierStyle {
+ ctor @androidx.compose.foundation.ExperimentalFoundationApi public MagnifierStyle(optional long size, optional float cornerRadius, optional float elevation, optional boolean clippingEnabled, optional boolean fishEyeEnabled);
+ method public boolean isSupported();
+ property public final boolean isSupported;
+ field public static final androidx.compose.foundation.MagnifierStyle.Companion Companion;
+ }
+
+ public static final class MagnifierStyle.Companion {
+ method public androidx.compose.foundation.MagnifierStyle getDefault();
+ method public androidx.compose.foundation.MagnifierStyle getTextDefault();
+ property public final androidx.compose.foundation.MagnifierStyle Default;
+ property public final androidx.compose.foundation.MagnifierStyle TextDefault;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @kotlin.jvm.JvmInline public final value class MarqueeAnimationMode {
+ field public static final androidx.compose.foundation.MarqueeAnimationMode.Companion Companion;
+ }
+
+ public static final class MarqueeAnimationMode.Companion {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public int getImmediately();
+ method @androidx.compose.foundation.ExperimentalFoundationApi public int getWhileFocused();
+ property @androidx.compose.foundation.ExperimentalFoundationApi public final int Immediately;
+ property @androidx.compose.foundation.ExperimentalFoundationApi public final int WhileFocused;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface MarqueeSpacing {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public int calculateSpacing(androidx.compose.ui.unit.Density, int contentWidth, int containerWidth);
+ field public static final androidx.compose.foundation.MarqueeSpacing.Companion Companion;
+ }
+
+ public static final class MarqueeSpacing.Companion {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.foundation.MarqueeSpacing fractionOfContainer(float fraction);
+ }
+
+ public enum MutatePriority {
+ method public static androidx.compose.foundation.MutatePriority valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.foundation.MutatePriority[] values();
+ enum_constant public static final androidx.compose.foundation.MutatePriority Default;
+ enum_constant public static final androidx.compose.foundation.MutatePriority PreventUserInput;
+ enum_constant public static final androidx.compose.foundation.MutatePriority UserInput;
+ }
+
+ @androidx.compose.runtime.Stable public final class MutatorMutex {
+ ctor public MutatorMutex();
+ method public suspend <R> Object? mutate(optional androidx.compose.foundation.MutatePriority priority, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
+ method public suspend <T, R> Object? mutateWith(T receiver, optional androidx.compose.foundation.MutatePriority priority, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class OverscrollConfiguration {
+ ctor public OverscrollConfiguration(optional long glowColor, optional androidx.compose.foundation.layout.PaddingValues drawPadding);
+ method public androidx.compose.foundation.layout.PaddingValues getDrawPadding();
+ method public long getGlowColor();
+ property public final androidx.compose.foundation.layout.PaddingValues drawPadding;
+ property public final long glowColor;
+ }
+
+ public final class OverscrollConfigurationKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> getLocalOverscrollConfiguration();
+ property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> LocalOverscrollConfiguration;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface OverscrollEffect {
+ method public suspend Object? applyToFling(long velocity, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Velocity,? super kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>,?> performFling, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public long applyToScroll(long delta, int source, kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset> performScroll);
+ method public androidx.compose.ui.Modifier getEffectModifier();
+ method public boolean isInProgress();
+ property public abstract androidx.compose.ui.Modifier effectModifier;
+ property public abstract boolean isInProgress;
+ }
+
+ public final class OverscrollKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier overscroll(androidx.compose.ui.Modifier, androidx.compose.foundation.OverscrollEffect overscrollEffect);
+ }
+
+ public final class ProgressSemanticsKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps);
+ }
+
+ public final class ScrollKt {
+ method public static androidx.compose.ui.Modifier horizontalScroll(androidx.compose.ui.Modifier, androidx.compose.foundation.ScrollState state, optional boolean enabled, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional boolean reverseScrolling);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.ScrollState rememberScrollState(optional int initial);
+ method public static androidx.compose.ui.Modifier verticalScroll(androidx.compose.ui.Modifier, androidx.compose.foundation.ScrollState state, optional boolean enabled, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional boolean reverseScrolling);
+ }
+
+ @androidx.compose.runtime.Stable public final class ScrollState implements androidx.compose.foundation.gestures.ScrollableState {
+ ctor public ScrollState(int initial);
+ method public suspend Object? animateScrollTo(int value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public float dispatchRawDelta(float delta);
+ method public androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public int getMaxValue();
+ method public int getValue();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? scrollTo(int value, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ property public boolean canScrollBackward;
+ property public boolean canScrollForward;
+ property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
+ property public boolean isScrollInProgress;
+ property public final int maxValue;
+ property public final int value;
+ field public static final androidx.compose.foundation.ScrollState.Companion Companion;
+ }
+
+ public static final class ScrollState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.ScrollState,?> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.ScrollState,?> Saver;
+ }
+
+ public final class SystemGestureExclusionKt {
+ method public static androidx.compose.ui.Modifier systemGestureExclusion(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier systemGestureExclusion(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
+ }
+
+}
+
+package androidx.compose.foundation.gestures {
+
+ public final class DragGestureDetectorKt {
+ method public static suspend Object? awaitDragOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitHorizontalDragOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitHorizontalTouchSlopOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitLongPressOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitTouchSlopOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitVerticalDragOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? awaitVerticalTouchSlopOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onTouchSlopReached, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? detectDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDragStart, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? detectDragGesturesAfterLongPress(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDragStart, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? detectHorizontalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDragStart, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onHorizontalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? detectVerticalDragGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit> onDragStart, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragEnd, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDragCancel, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputChange,? super java.lang.Float,kotlin.Unit> onVerticalDrag, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? drag(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public static suspend Object? horizontalDrag(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public static suspend Object? verticalDrag(androidx.compose.ui.input.pointer.AwaitPointerEventScope, long pointerId, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.pointer.PointerInputChange,kotlin.Unit> onDrag, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public interface DragScope {
+ method public void dragBy(float pixels);
+ }
+
+ public final class DraggableKt {
+ method public static androidx.compose.foundation.gestures.DraggableState DraggableState(kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDelta);
+ method public static androidx.compose.ui.Modifier draggable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.DraggableState state, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional boolean startDragImmediately, optional kotlin.jvm.functions.Function3<? super kotlinx.coroutines.CoroutineScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onDragStarted, optional kotlin.jvm.functions.Function3<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Float,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onDragStopped, optional boolean reverseDirection);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.DraggableState rememberDraggableState(kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDelta);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface DraggableState {
+ method public void dispatchRawDelta(float delta);
+ method public suspend Object? drag(optional androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ @androidx.compose.runtime.Stable public interface FlingBehavior {
+ method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ }
+
+ public final class ForEachGestureKt {
+ method public static suspend Object? awaitEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @Deprecated public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class GestureCancellationException extends java.util.concurrent.CancellationException {
+ ctor public GestureCancellationException(optional String? message);
+ }
+
+ public enum Orientation {
+ method public static androidx.compose.foundation.gestures.Orientation valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.foundation.gestures.Orientation[] values();
+ enum_constant public static final androidx.compose.foundation.gestures.Orientation Horizontal;
+ enum_constant public static final androidx.compose.foundation.gestures.Orientation Vertical;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface PressGestureScope extends androidx.compose.ui.unit.Density {
+ method public suspend Object? awaitRelease(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? tryAwaitRelease(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public final class ScrollExtensionsKt {
+ method public static suspend Object? animateScrollBy(androidx.compose.foundation.gestures.ScrollableState, float value, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super java.lang.Float>);
+ method public static suspend Object? scrollBy(androidx.compose.foundation.gestures.ScrollableState, float value, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ method public static suspend Object? stopScroll(androidx.compose.foundation.gestures.ScrollableState, optional androidx.compose.foundation.MutatePriority scrollPriority, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public interface ScrollScope {
+ method public float scrollBy(float pixels);
+ }
+
+ public final class ScrollableDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior();
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public androidx.compose.foundation.OverscrollEffect overscrollEffect();
+ method public boolean reverseDirection(androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.foundation.gestures.Orientation orientation, boolean reverseScrolling);
+ field public static final androidx.compose.foundation.gestures.ScrollableDefaults INSTANCE;
+ }
+
+ public final class ScrollableKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, androidx.compose.foundation.OverscrollEffect? overscrollEffect, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+ method public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ScrollableState {
+ method public float dispatchRawDelta(float delta);
+ method public default boolean getCanScrollBackward();
+ method public default boolean getCanScrollForward();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(optional androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public default boolean canScrollBackward;
+ property public default boolean canScrollForward;
+ property public abstract boolean isScrollInProgress;
+ }
+
+ public final class ScrollableStateKt {
+ method public static androidx.compose.foundation.gestures.ScrollableState ScrollableState(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.ScrollableState rememberScrollableState(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> consumeScrollDelta);
+ }
+
+ public final class TapGestureDetectorKt {
+ method @Deprecated public static suspend androidx.compose.ui.input.pointer.PointerInputChange awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed);
+ method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPress, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onTap, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @Deprecated public static suspend androidx.compose.ui.input.pointer.PointerInputChange? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope);
+ method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
+ }
+
+ public final class TransformGestureDetectorKt {
+ method public static long calculateCentroid(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
+ method public static float calculateCentroidSize(androidx.compose.ui.input.pointer.PointerEvent, optional boolean useCurrent);
+ method public static long calculatePan(androidx.compose.ui.input.pointer.PointerEvent);
+ method public static float calculateRotation(androidx.compose.ui.input.pointer.PointerEvent);
+ method public static float calculateZoom(androidx.compose.ui.input.pointer.PointerEvent);
+ method public static suspend Object? detectTransformGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional boolean panZoomLock, kotlin.jvm.functions.Function4<? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,? super java.lang.Float,kotlin.Unit> onGesture, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface TransformScope {
+ method public void transformBy(optional float zoomChange, optional long panChange, optional float rotationChange);
+ }
+
+ public final class TransformableKt {
+ method public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, optional boolean lockRotationOnZoomPan, optional boolean enabled);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, kotlin.jvm.functions.Function0<java.lang.Boolean> canPan, optional boolean lockRotationOnZoomPan, optional boolean enabled);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface TransformableState {
+ method public boolean isTransformInProgress();
+ method public suspend Object? transform(optional androidx.compose.foundation.MutatePriority transformPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.TransformScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public abstract boolean isTransformInProgress;
+ }
+
+ public final class TransformableStateKt {
+ method public static androidx.compose.foundation.gestures.TransformableState TransformableState(kotlin.jvm.functions.Function3<? super java.lang.Float,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,kotlin.Unit> onTransformation);
+ method public static suspend Object? animatePanBy(androidx.compose.foundation.gestures.TransformableState, long offset, optional androidx.compose.animation.core.AnimationSpec<androidx.compose.ui.geometry.Offset> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? animateRotateBy(androidx.compose.foundation.gestures.TransformableState, float degrees, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? animateZoomBy(androidx.compose.foundation.gestures.TransformableState, float zoomFactor, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? panBy(androidx.compose.foundation.gestures.TransformableState, long offset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.TransformableState rememberTransformableState(kotlin.jvm.functions.Function3<? super java.lang.Float,? super androidx.compose.ui.geometry.Offset,? super java.lang.Float,kotlin.Unit> onTransformation);
+ method public static suspend Object? rotateBy(androidx.compose.foundation.gestures.TransformableState, float degrees, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? stopTransformation(androidx.compose.foundation.gestures.TransformableState, optional androidx.compose.foundation.MutatePriority terminationPriority, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? zoomBy(androidx.compose.foundation.gestures.TransformableState, float zoomFactor, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+}
+
+package androidx.compose.foundation.gestures.snapping {
+
+ public final class LazyGridSnapLayoutInfoProviderKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+ }
+
+ public final class LazyListSnapLayoutInfoProviderKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+ ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+ method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onSettlingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ }
+
+ public final class SnapFlingBehaviorKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
+ method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
+ method public float calculateSnapStepSize(androidx.compose.ui.unit.Density);
+ method public float calculateSnappingOffset(androidx.compose.ui.unit.Density, float currentVelocity);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
+ method public int position(androidx.compose.ui.unit.Density, int layoutSize, int itemSize, int itemIndex);
+ field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
+ }
+
+ public static final class SnapPositionInLayout.Companion {
+ method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
+ property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
+ }
+
+}
+
+package androidx.compose.foundation.interaction {
+
+ public interface DragInteraction extends androidx.compose.foundation.interaction.Interaction {
+ }
+
+ public static final class DragInteraction.Cancel implements androidx.compose.foundation.interaction.DragInteraction {
+ ctor public DragInteraction.Cancel(androidx.compose.foundation.interaction.DragInteraction.Start start);
+ method public androidx.compose.foundation.interaction.DragInteraction.Start getStart();
+ property public final androidx.compose.foundation.interaction.DragInteraction.Start start;
+ }
+
+ public static final class DragInteraction.Start implements androidx.compose.foundation.interaction.DragInteraction {
+ ctor public DragInteraction.Start();
+ }
+
+ public static final class DragInteraction.Stop implements androidx.compose.foundation.interaction.DragInteraction {
+ ctor public DragInteraction.Stop(androidx.compose.foundation.interaction.DragInteraction.Start start);
+ method public androidx.compose.foundation.interaction.DragInteraction.Start getStart();
+ property public final androidx.compose.foundation.interaction.DragInteraction.Start start;
+ }
+
+ public final class DragInteractionKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> collectIsDraggedAsState(androidx.compose.foundation.interaction.InteractionSource);
+ }
+
+ public interface FocusInteraction extends androidx.compose.foundation.interaction.Interaction {
+ }
+
+ public static final class FocusInteraction.Focus implements androidx.compose.foundation.interaction.FocusInteraction {
+ ctor public FocusInteraction.Focus();
+ }
+
+ public static final class FocusInteraction.Unfocus implements androidx.compose.foundation.interaction.FocusInteraction {
+ ctor public FocusInteraction.Unfocus(androidx.compose.foundation.interaction.FocusInteraction.Focus focus);
+ method public androidx.compose.foundation.interaction.FocusInteraction.Focus getFocus();
+ property public final androidx.compose.foundation.interaction.FocusInteraction.Focus focus;
+ }
+
+ public final class FocusInteractionKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> collectIsFocusedAsState(androidx.compose.foundation.interaction.InteractionSource);
+ }
+
+ public interface HoverInteraction extends androidx.compose.foundation.interaction.Interaction {
+ }
+
+ public static final class HoverInteraction.Enter implements androidx.compose.foundation.interaction.HoverInteraction {
+ ctor public HoverInteraction.Enter();
+ }
+
+ public static final class HoverInteraction.Exit implements androidx.compose.foundation.interaction.HoverInteraction {
+ ctor public HoverInteraction.Exit(androidx.compose.foundation.interaction.HoverInteraction.Enter enter);
+ method public androidx.compose.foundation.interaction.HoverInteraction.Enter getEnter();
+ property public final androidx.compose.foundation.interaction.HoverInteraction.Enter enter;
+ }
+
+ public final class HoverInteractionKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> collectIsHoveredAsState(androidx.compose.foundation.interaction.InteractionSource);
+ }
+
+ public interface Interaction {
+ }
+
+ @androidx.compose.runtime.Stable public interface InteractionSource {
+ method public kotlinx.coroutines.flow.Flow<androidx.compose.foundation.interaction.Interaction> getInteractions();
+ property public abstract kotlinx.coroutines.flow.Flow<androidx.compose.foundation.interaction.Interaction> interactions;
+ }
+
+ public final class InteractionSourceKt {
+ method public static androidx.compose.foundation.interaction.MutableInteractionSource MutableInteractionSource();
+ }
+
+ @androidx.compose.runtime.Stable public interface MutableInteractionSource extends androidx.compose.foundation.interaction.InteractionSource {
+ method public suspend Object? emit(androidx.compose.foundation.interaction.Interaction interaction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean tryEmit(androidx.compose.foundation.interaction.Interaction interaction);
+ }
+
+ public interface PressInteraction extends androidx.compose.foundation.interaction.Interaction {
+ }
+
+ public static final class PressInteraction.Cancel implements androidx.compose.foundation.interaction.PressInteraction {
+ ctor public PressInteraction.Cancel(androidx.compose.foundation.interaction.PressInteraction.Press press);
+ method public androidx.compose.foundation.interaction.PressInteraction.Press getPress();
+ property public final androidx.compose.foundation.interaction.PressInteraction.Press press;
+ }
+
+ public static final class PressInteraction.Press implements androidx.compose.foundation.interaction.PressInteraction {
+ ctor public PressInteraction.Press(long pressPosition);
+ method public long getPressPosition();
+ property public final long pressPosition;
+ }
+
+ public static final class PressInteraction.Release implements androidx.compose.foundation.interaction.PressInteraction {
+ ctor public PressInteraction.Release(androidx.compose.foundation.interaction.PressInteraction.Press press);
+ method public androidx.compose.foundation.interaction.PressInteraction.Press getPress();
+ property public final androidx.compose.foundation.interaction.PressInteraction.Press press;
+ }
+
+ public final class PressInteractionKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> collectIsPressedAsState(androidx.compose.foundation.interaction.InteractionSource);
+ }
+
+}
+
+package androidx.compose.foundation.lazy {
+
+ 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, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+ method @Deprecated @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, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
+ method @Deprecated @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);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method @Deprecated public static inline <T> void items(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method @Deprecated public static inline <T> void items(androidx.compose.foundation.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method @Deprecated public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method @Deprecated public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.compose.foundation.lazy.LazyScopeMarker @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyItemScope {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
+ method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ }
+
+ public interface LazyListItemInfo {
+ method public default Object? getContentType();
+ method public int getIndex();
+ method public Object getKey();
+ method public int getOffset();
+ method public int getSize();
+ property public default Object? contentType;
+ property public abstract int index;
+ property public abstract Object key;
+ property public abstract int offset;
+ property public abstract int size;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListLayoutInfo {
+ method public default int getAfterContentPadding();
+ method public default int getBeforeContentPadding();
+ method public default int getMainAxisItemSpacing();
+ method public default androidx.compose.foundation.gestures.Orientation getOrientation();
+ method public default boolean getReverseLayout();
+ method public int getTotalItemsCount();
+ method public int getViewportEndOffset();
+ method public default long getViewportSize();
+ method public int getViewportStartOffset();
+ method public java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> getVisibleItemsInfo();
+ property public default int afterContentPadding;
+ property public default int beforeContentPadding;
+ property public default int mainAxisItemSpacing;
+ property public default androidx.compose.foundation.gestures.Orientation orientation;
+ property public default boolean reverseLayout;
+ property public abstract int totalItemsCount;
+ property public abstract int viewportEndOffset;
+ property public default long viewportSize;
+ property public abstract int viewportStartOffset;
+ property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
+ }
+
+ @androidx.compose.foundation.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+ method public default void item(optional Object? key, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+ method @Deprecated public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+ method public default void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ method @Deprecated 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, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
+ }
+
+ @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>);
+ method public float dispatchRawDelta(float delta);
+ method public int getFirstVisibleItemIndex();
+ method public int getFirstVisibleItemScrollOffset();
+ method public androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public boolean canScrollBackward;
+ property public boolean canScrollForward;
+ property public final int firstVisibleItemIndex;
+ property public final int firstVisibleItemScrollOffset;
+ property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
+ property public boolean isScrollInProgress;
+ property public final androidx.compose.foundation.lazy.LazyListLayoutInfo layoutInfo;
+ field public static final androidx.compose.foundation.lazy.LazyListState.Companion Companion;
+ }
+
+ public static final class LazyListState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.LazyListState,?> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.LazyListState,?> Saver;
+ }
+
+ public final class LazyListStateKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.lazy.LazyListState rememberLazyListState(optional int initialFirstVisibleItemIndex, optional int initialFirstVisibleItemScrollOffset);
+ }
+
+ @kotlin.DslMarker public @interface LazyScopeMarker {
+ }
+
+}
+
+package androidx.compose.foundation.lazy.grid {
+
+ @androidx.compose.runtime.Stable public interface GridCells {
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class GridCells.Adaptive implements androidx.compose.foundation.lazy.grid.GridCells {
+ ctor public GridCells.Adaptive(float minSize);
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class GridCells.Fixed implements androidx.compose.foundation.lazy.grid.GridCells {
+ ctor public GridCells.Fixed(int count);
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+ ctor public GridCells.FixedSize(float size);
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
+ method public int getCurrentLineSpan();
+ property @androidx.compose.foundation.ExperimentalFoundationApi public final int currentLineSpan;
+ }
+
+ public final class LazyGridDslKt {
+ method @androidx.compose.runtime.Composable public static void LazyHorizontalGrid(androidx.compose.foundation.lazy.grid.GridCells rows, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.grid.LazyGridState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.grid.LazyGridScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.compose.foundation.lazy.grid.GridCells columns, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.grid.LazyGridState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.grid.LazyGridScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.grid.LazyGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,? super T,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional kotlin.jvm.functions.Function1<? super T,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.grid.LazyGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,? super T,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional kotlin.jvm.functions.Function1<? super T,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.grid.LazyGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,? super java.lang.Integer,? super T,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.grid.LazyGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,? super java.lang.Integer,? super T,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ public sealed interface LazyGridItemInfo {
+ method public int getColumn();
+ method public Object? getContentType();
+ method public int getIndex();
+ method public Object getKey();
+ method public long getOffset();
+ method public int getRow();
+ method public long getSize();
+ property public abstract int column;
+ property public abstract Object? contentType;
+ property public abstract int index;
+ property public abstract Object key;
+ property public abstract long offset;
+ property public abstract int row;
+ property public abstract long size;
+ field public static final androidx.compose.foundation.lazy.grid.LazyGridItemInfo.Companion Companion;
+ field public static final int UnknownColumn = -1; // 0xffffffff
+ field public static final int UnknownRow = -1; // 0xffffffff
+ }
+
+ public static final class LazyGridItemInfo.Companion {
+ field public static final int UnknownColumn = -1; // 0xffffffff
+ field public static final int UnknownRow = -1; // 0xffffffff
+ }
+
+ @androidx.compose.foundation.lazy.grid.LazyGridScopeMarker @androidx.compose.runtime.Stable public sealed interface LazyGridItemScope {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
+ }
+
+ @androidx.compose.foundation.lazy.grid.LazyGridScopeMarker public sealed interface LazyGridItemSpanScope {
+ method public int getMaxCurrentLineSpan();
+ method public int getMaxLineSpan();
+ property public abstract int maxCurrentLineSpan;
+ property public abstract int maxLineSpan;
+ }
+
+ public sealed interface LazyGridLayoutInfo {
+ method public int getAfterContentPadding();
+ method public int getBeforeContentPadding();
+ method public int getMainAxisItemSpacing();
+ method public androidx.compose.foundation.gestures.Orientation getOrientation();
+ method public boolean getReverseLayout();
+ method public int getTotalItemsCount();
+ method public int getViewportEndOffset();
+ method public long getViewportSize();
+ method public int getViewportStartOffset();
+ method public java.util.List<androidx.compose.foundation.lazy.grid.LazyGridItemInfo> getVisibleItemsInfo();
+ property public abstract int afterContentPadding;
+ property public abstract int beforeContentPadding;
+ property public abstract int mainAxisItemSpacing;
+ property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+ property public abstract boolean reverseLayout;
+ property public abstract int totalItemsCount;
+ property public abstract int viewportEndOffset;
+ property public abstract long viewportSize;
+ property public abstract int viewportStartOffset;
+ property public abstract java.util.List<androidx.compose.foundation.lazy.grid.LazyGridItemInfo> visibleItemsInfo;
+ }
+
+ @androidx.compose.foundation.lazy.grid.LazyGridScopeMarker public sealed interface LazyGridScope {
+ method public void item(optional Object? key, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemSpanScope,? super java.lang.Integer,androidx.compose.foundation.lazy.grid.GridItemSpan>? span, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.grid.LazyGridItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ }
+
+ @kotlin.DslMarker public @interface LazyGridScopeMarker {
+ }
+
+ public final class LazyGridSpanKt {
+ method public static long GridItemSpan(int currentLineSpan);
+ }
+
+ @androidx.compose.runtime.Stable public final class LazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
+ ctor public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
+ method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public float dispatchRawDelta(float delta);
+ method public int getFirstVisibleItemIndex();
+ method public int getFirstVisibleItemScrollOffset();
+ method public androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo getLayoutInfo();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public boolean canScrollBackward;
+ property public boolean canScrollForward;
+ property public final int firstVisibleItemIndex;
+ property public final int firstVisibleItemScrollOffset;
+ property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
+ property public boolean isScrollInProgress;
+ property public final androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo layoutInfo;
+ field public static final androidx.compose.foundation.lazy.grid.LazyGridState.Companion Companion;
+ }
+
+ public static final class LazyGridState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.grid.LazyGridState,?> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.grid.LazyGridState,?> Saver;
+ }
+
+ public final class LazyGridStateKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.lazy.grid.LazyGridState rememberLazyGridState(optional int initialFirstVisibleItemIndex, optional int initialFirstVisibleItemScrollOffset);
+ }
+
+}
+
+package androidx.compose.foundation.lazy.layout {
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface IntervalList<T> {
+ method public void forEach(optional int fromIndex, optional int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
+ method public operator androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
+ method public int getSize();
+ property public abstract int size;
+ }
+
+ public static final class IntervalList.Interval<T> {
+ method public int getSize();
+ method public int getStartIndex();
+ method public T getValue();
+ property public final int size;
+ property public final int startIndex;
+ property public final T value;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public abstract class LazyLayoutIntervalContent<Interval extends androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.Interval> {
+ ctor public LazyLayoutIntervalContent();
+ method public final Object? getContentType(int index);
+ method public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> getIntervals();
+ method public final int getItemCount();
+ method public final Object getKey(int index);
+ method public final inline <T> T withInterval(int globalIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super Interval,? extends T> block);
+ property public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> intervals;
+ property public final int itemCount;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public static interface LazyLayoutIntervalContent.Interval {
+ method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? getKey();
+ method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> getType();
+ property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? key;
+ property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> type;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface LazyLayoutItemProvider {
+ method @androidx.compose.runtime.Composable public void Item(int index, Object key);
+ method public default Object? getContentType(int index);
+ method public default int getIndex(Object key);
+ method public int getItemCount();
+ method public default Object getKey(int index);
+ property public abstract int itemCount;
+ }
+
+ public final class LazyLayoutItemProviderKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
+ }
+
+ public final class LazyLayoutKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface LazyLayoutMeasureScope extends androidx.compose.ui.layout.MeasureScope {
+ method public java.util.List<androidx.compose.ui.layout.Placeable> measure(int index, long constraints);
+ method @androidx.compose.runtime.Stable public default float toDp(float);
+ method @androidx.compose.runtime.Stable public default float toDp(int);
+ method @androidx.compose.runtime.Stable public default float toDp(long);
+ method @androidx.compose.runtime.Stable public default long toDpSize(long);
+ method @androidx.compose.runtime.Stable public default long toSize(long);
+ method @androidx.compose.runtime.Stable public default long toSp(float);
+ method @androidx.compose.runtime.Stable public default long toSp(float);
+ method @androidx.compose.runtime.Stable public default long toSp(int);
+ }
+
+ public final class LazyLayoutPinnableItemKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayoutPinnableItem(Object? key, int index, androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList pinnedItemList, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class LazyLayoutPinnedItemList implements kotlin.jvm.internal.markers.KMappedMarker java.util.List<androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList.PinnedItem> {
+ ctor public LazyLayoutPinnedItemList();
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public static sealed interface LazyLayoutPinnedItemList.PinnedItem {
+ method public int getIndex();
+ method public Object? getKey();
+ property public abstract int index;
+ property public abstract Object? key;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class LazyLayoutPrefetchState {
+ ctor public LazyLayoutPrefetchState();
+ method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index, long constraints);
+ }
+
+ public static sealed interface LazyLayoutPrefetchState.PrefetchHandle {
+ method public void cancel();
+ }
+
+ public final class Lazy_androidKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class MutableIntervalList<T> implements androidx.compose.foundation.lazy.layout.IntervalList<T> {
+ ctor public MutableIntervalList();
+ method public void addInterval(int size, T value);
+ method public void forEach(int fromIndex, int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
+ method public androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
+ method public int getSize();
+ property public int size;
+ }
+
+}
+
+package androidx.compose.foundation.lazy.staggeredgrid {
+
+ public final class LazyStaggeredGridDslKt {
+ method @androidx.compose.runtime.Composable public static void LazyHorizontalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells rows, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional float horizontalItemSpacing, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void LazyVerticalStaggeredGrid(androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells columns, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional float verticalItemSpacing, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,?> contentType, optional kotlin.jvm.functions.Function1<? super T,androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan>? span, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,?> contentType, optional kotlin.jvm.functions.Function1<? super T,androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan>? span, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan>? span, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?> contentType, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan>? span, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ public sealed interface LazyStaggeredGridItemInfo {
+ method public Object? getContentType();
+ method public int getIndex();
+ method public Object getKey();
+ method public int getLane();
+ method public long getOffset();
+ method public long getSize();
+ property public abstract Object? contentType;
+ property public abstract int index;
+ property public abstract Object key;
+ property public abstract int lane;
+ property public abstract long offset;
+ property public abstract long size;
+ }
+
+ @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
+ }
+
+ public sealed interface LazyStaggeredGridLayoutInfo {
+ method public int getAfterContentPadding();
+ method public int getBeforeContentPadding();
+ method public int getMainAxisItemSpacing();
+ method public androidx.compose.foundation.gestures.Orientation getOrientation();
+ method public int getTotalItemsCount();
+ method public int getViewportEndOffset();
+ method public long getViewportSize();
+ method public int getViewportStartOffset();
+ method public java.util.List<androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemInfo> getVisibleItemsInfo();
+ property public abstract int afterContentPadding;
+ property public abstract int beforeContentPadding;
+ property public abstract int mainAxisItemSpacing;
+ property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+ property public abstract int totalItemsCount;
+ property public abstract int viewportEndOffset;
+ property public abstract long viewportSize;
+ property public abstract int viewportStartOffset;
+ property public abstract java.util.List<androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemInfo> visibleItemsInfo;
+ }
+
+ public sealed interface LazyStaggeredGridScope {
+ method public void item(optional Object? key, optional Object? contentType, optional androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan? span, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?> contentType, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan>? span, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ }
+
+ public final class LazyStaggeredGridState implements androidx.compose.foundation.gestures.ScrollableState {
+ ctor public LazyStaggeredGridState(optional int initialFirstVisibleItemIndex, optional int initialFirstVisibleItemOffset);
+ method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public float dispatchRawDelta(float delta);
+ method public int getFirstVisibleItemIndex();
+ method public int getFirstVisibleItemScrollOffset();
+ method public androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLayoutInfo getLayoutInfo();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public boolean canScrollBackward;
+ property public boolean canScrollForward;
+ property public final int firstVisibleItemIndex;
+ property public final int firstVisibleItemScrollOffset;
+ property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
+ property public boolean isScrollInProgress;
+ property public final androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLayoutInfo layoutInfo;
+ field public static final androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState.Companion Companion;
+ }
+
+ public static final class LazyStaggeredGridState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,java.lang.Object> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState,java.lang.Object> Saver;
+ }
+
+ public final class LazyStaggeredGridStateKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState rememberLazyStaggeredGridState(optional int initialFirstVisibleItemIndex, optional int initialFirstVisibleItemScrollOffset);
+ }
+
+ @androidx.compose.runtime.Stable public interface StaggeredGridCells {
+ method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class StaggeredGridCells.Adaptive implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
+ ctor public StaggeredGridCells.Adaptive(float minSize);
+ method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class StaggeredGridCells.Fixed implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
+ ctor public StaggeredGridCells.Fixed(int count);
+ method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public static final class StaggeredGridCells.FixedSize implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
+ ctor public StaggeredGridCells.FixedSize(float size);
+ method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
+ public final class StaggeredGridItemSpan {
+ field public static final androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan.Companion Companion;
+ }
+
+ public static final class StaggeredGridItemSpan.Companion {
+ method public androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan getFullLine();
+ method public androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan getSingleLane();
+ property public final androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan FullLine;
+ property public final androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan SingleLane;
+ }
+
+}
+
+package androidx.compose.foundation.pager {
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PageSize {
+ method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fill implements androidx.compose.foundation.pager.PageSize {
+ method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
+ field public static final androidx.compose.foundation.pager.PageSize.Fill INSTANCE;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fixed implements androidx.compose.foundation.pager.PageSize {
+ ctor public PageSize.Fixed(float pageSize);
+ method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
+ method public float getPageSize();
+ property public final float pageSize;
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class PagerDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional float snapVelocityThreshold, optional float snapPositionalThreshold);
+ method public androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection(androidx.compose.foundation.gestures.Orientation orientation);
+ field public static final androidx.compose.foundation.pager.PagerDefaults INSTANCE;
+ }
+
+ public final class PagerKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PagerSnapDistance {
+ method public int calculateTargetPage(int startPage, int suggestedTargetPage, float velocity, int pageSize, int pageSpacing);
+ field public static final androidx.compose.foundation.pager.PagerSnapDistance.Companion Companion;
+ }
+
+ public static final class PagerSnapDistance.Companion {
+ method public androidx.compose.foundation.pager.PagerSnapDistance atMost(int pages);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public abstract class PagerState implements androidx.compose.foundation.gestures.ScrollableState {
+ ctor public PagerState(optional int initialPage, optional float initialPageOffsetFraction);
+ method public final suspend Object? animateScrollToPage(int page, optional float pageOffsetFraction, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public float dispatchRawDelta(float delta);
+ method public final boolean getCanScrollBackward();
+ method public final boolean getCanScrollForward();
+ method public final int getCurrentPage();
+ method public final float getCurrentPageOffsetFraction();
+ method public final int getInitialPage();
+ method public final float getInitialPageOffsetFraction();
+ method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public final float getOffsetFractionForPage(int page);
+ method public abstract int getPageCount();
+ method public final int getSettledPage();
+ method public final int getTargetPage();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final suspend Object? scrollToPage(int page, optional float pageOffsetFraction, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final boolean canScrollBackward;
+ property public final boolean canScrollForward;
+ property public final int currentPage;
+ property public final float currentPageOffsetFraction;
+ property public final int initialPage;
+ property public final float initialPageOffsetFraction;
+ property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
+ property public boolean isScrollInProgress;
+ property public abstract int pageCount;
+ property public final int settledPage;
+ property public final int targetPage;
+ }
+
+ public final class PagerStateKt {
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction, kotlin.jvm.functions.Function0<java.lang.Integer> pageCount);
+ }
+
+}
+
+package androidx.compose.foundation.relocation {
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
+ method public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class BringIntoViewRequesterKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.relocation.BringIntoViewRequester BringIntoViewRequester();
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewRequester(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewRequester bringIntoViewRequester);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public interface BringIntoViewResponder {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public suspend Object? bringChildIntoView(kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Rect> localRect, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
+ }
+
+ public final class BringIntoViewResponderKt {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewResponder(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewResponder responder);
+ }
+
+}
+
+package androidx.compose.foundation.selection {
+
+ public final class SelectableGroupKt {
+ method public static androidx.compose.ui.Modifier selectableGroup(androidx.compose.ui.Modifier);
+ }
+
+ public final class SelectableKt {
+ method public static androidx.compose.ui.Modifier selectable(androidx.compose.ui.Modifier, boolean selected, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public static androidx.compose.ui.Modifier selectable(androidx.compose.ui.Modifier, boolean selected, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ }
+
+ public final class ToggleableKt {
+ method public static androidx.compose.ui.Modifier toggleable(androidx.compose.ui.Modifier, boolean value, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onValueChange);
+ method public static androidx.compose.ui.Modifier toggleable(androidx.compose.ui.Modifier, boolean value, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onValueChange);
+ method public static androidx.compose.ui.Modifier triStateToggleable(androidx.compose.ui.Modifier, androidx.compose.ui.state.ToggleableState state, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public static androidx.compose.ui.Modifier triStateToggleable(androidx.compose.ui.Modifier, androidx.compose.ui.state.ToggleableState state, optional boolean enabled, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ }
+
+}
+
+package androidx.compose.foundation.shape {
+
+ public final class AbsoluteCutCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
+ ctor public AbsoluteCutCornerShape(androidx.compose.foundation.shape.CornerSize topLeft, androidx.compose.foundation.shape.CornerSize topRight, androidx.compose.foundation.shape.CornerSize bottomRight, androidx.compose.foundation.shape.CornerSize bottomLeft);
+ method public androidx.compose.foundation.shape.AbsoluteCutCornerShape copy(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, float topStart, float topEnd, float bottomEnd, float bottomStart, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ }
+
+ public final class AbsoluteCutCornerShapeKt {
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(androidx.compose.foundation.shape.CornerSize corner);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(float size);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(float size);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(int percent);
+ method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+ }
+
+ public final class AbsoluteRoundedCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
+ ctor public AbsoluteRoundedCornerShape(androidx.compose.foundation.shape.CornerSize topLeft, androidx.compose.foundation.shape.CornerSize topRight, androidx.compose.foundation.shape.CornerSize bottomRight, androidx.compose.foundation.shape.CornerSize bottomLeft);
+ method public androidx.compose.foundation.shape.AbsoluteRoundedCornerShape copy(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, float topStart, float topEnd, float bottomEnd, float bottomStart, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ }
+
+ public final class AbsoluteRoundedCornerShapeKt {
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(androidx.compose.foundation.shape.CornerSize corner);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(float size);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(float size);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(int percent);
+ method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+ }
+
+ public abstract class CornerBasedShape implements androidx.compose.ui.graphics.Shape {
+ ctor public CornerBasedShape(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public final androidx.compose.foundation.shape.CornerBasedShape copy(androidx.compose.foundation.shape.CornerSize all);
+ method public abstract androidx.compose.foundation.shape.CornerBasedShape copy(optional androidx.compose.foundation.shape.CornerSize topStart, optional androidx.compose.foundation.shape.CornerSize topEnd, optional androidx.compose.foundation.shape.CornerSize bottomEnd, optional androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public final androidx.compose.ui.graphics.Outline createOutline(long size, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.unit.Density density);
+ method public abstract androidx.compose.ui.graphics.Outline createOutline(long size, float topStart, float topEnd, float bottomEnd, float bottomStart, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public final androidx.compose.foundation.shape.CornerSize getBottomEnd();
+ method public final androidx.compose.foundation.shape.CornerSize getBottomStart();
+ method public final androidx.compose.foundation.shape.CornerSize getTopEnd();
+ method public final androidx.compose.foundation.shape.CornerSize getTopStart();
+ property public final androidx.compose.foundation.shape.CornerSize bottomEnd;
+ property public final androidx.compose.foundation.shape.CornerSize bottomStart;
+ property public final androidx.compose.foundation.shape.CornerSize topEnd;
+ property public final androidx.compose.foundation.shape.CornerSize topStart;
+ }
+
+ @androidx.compose.runtime.Immutable public interface CornerSize {
+ method public float toPx(long shapeSize, androidx.compose.ui.unit.Density density);
+ }
+
+ public final class CornerSizeKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(int percent);
+ method public static androidx.compose.foundation.shape.CornerSize getZeroCornerSize();
+ property public static final androidx.compose.foundation.shape.CornerSize ZeroCornerSize;
+ }
+
+ public final class CutCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
+ ctor public CutCornerShape(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.foundation.shape.CutCornerShape copy(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, float topStart, float topEnd, float bottomEnd, float bottomStart, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ }
+
+ public final class CutCornerShapeKt {
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(androidx.compose.foundation.shape.CornerSize corner);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(float size);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(float size);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(int percent);
+ method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+ }
+
+ public final class GenericShape implements androidx.compose.ui.graphics.Shape {
+ ctor public GenericShape(kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.Path,? super androidx.compose.ui.geometry.Size,? super androidx.compose.ui.unit.LayoutDirection,kotlin.Unit> builder);
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.unit.Density density);
+ }
+
+ public final class RoundedCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
+ ctor public RoundedCornerShape(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.foundation.shape.RoundedCornerShape copy(androidx.compose.foundation.shape.CornerSize topStart, androidx.compose.foundation.shape.CornerSize topEnd, androidx.compose.foundation.shape.CornerSize bottomEnd, androidx.compose.foundation.shape.CornerSize bottomStart);
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, float topStart, float topEnd, float bottomEnd, float bottomStart, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ }
+
+ public final class RoundedCornerShapeKt {
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(androidx.compose.foundation.shape.CornerSize corner);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(float size);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(float size);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(int percent);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+ method public static androidx.compose.foundation.shape.RoundedCornerShape getCircleShape();
+ property public static final androidx.compose.foundation.shape.RoundedCornerShape CircleShape;
+ }
+
+}
+
+package androidx.compose.foundation.text {
+
+ public final class BasicTextFieldKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+ method @androidx.compose.runtime.Composable public static void BasicTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+ method @androidx.compose.runtime.Composable public static void BasicTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
+ }
+
+ public final class BasicTextKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional androidx.compose.ui.graphics.ColorProducer? color);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
+ method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+ method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional androidx.compose.ui.graphics.ColorProducer? color);
+ }
+
+ public final class ClickableTextKt {
+ method @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onHover, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
+ }
+
+ @androidx.compose.runtime.Immutable public final class InlineTextContent {
+ ctor public InlineTextContent(androidx.compose.ui.text.Placeholder placeholder, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> children);
+ method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit> getChildren();
+ method public androidx.compose.ui.text.Placeholder getPlaceholder();
+ property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit> children;
+ property public final androidx.compose.ui.text.Placeholder placeholder;
+ }
+
+ public final class InlineTextContentKt {
+ method public static void appendInlineContent(androidx.compose.ui.text.AnnotatedString.Builder, String id, optional String alternateText);
+ }
+
+ @kotlin.RequiresOptIn(message="Internal/Unstable API for use only between foundation modules sharing " + "the same exact version, subject to change without notice.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationTextApi {
+ }
+
+ public interface KeyboardActionScope {
+ method public void defaultKeyboardAction(int imeAction);
+ }
+
+ @androidx.compose.runtime.Stable public final class KeyboardActions {
+ ctor public KeyboardActions(optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onDone, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onGo, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onNext, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onPrevious, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSearch, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSend);
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnDone();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnGo();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnNext();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnPrevious();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnSearch();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnSend();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onDone;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onGo;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onNext;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onPrevious;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSearch;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSend;
+ field public static final androidx.compose.foundation.text.KeyboardActions.Companion Companion;
+ }
+
+ public static final class KeyboardActions.Companion {
+ method public androidx.compose.foundation.text.KeyboardActions getDefault();
+ property public final androidx.compose.foundation.text.KeyboardActions Default;
+ }
+
+ public final class KeyboardActionsKt {
+ method public static androidx.compose.foundation.text.KeyboardActions KeyboardActions(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit> onAny);
+ }
+
+ @androidx.compose.runtime.Immutable public final class KeyboardOptions {
+ ctor public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
+ method public androidx.compose.foundation.text.KeyboardOptions copy(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
+ method public boolean getAutoCorrect();
+ method public int getCapitalization();
+ method public int getImeAction();
+ method public int getKeyboardType();
+ property public final boolean autoCorrect;
+ property public final int capitalization;
+ property public final int imeAction;
+ property public final int keyboardType;
+ field public static final androidx.compose.foundation.text.KeyboardOptions.Companion Companion;
+ }
+
+ public static final class KeyboardOptions.Companion {
+ method public androidx.compose.foundation.text.KeyboardOptions getDefault();
+ property public final androidx.compose.foundation.text.KeyboardOptions Default;
+ }
+
+}
+
+package androidx.compose.foundation.text.selection {
+
+ public final class SelectionContainerKt {
+ method @androidx.compose.runtime.Composable public static void DisableSelection(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void SelectionContainer(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextSelectionColors {
+ ctor public TextSelectionColors(long handleColor, long backgroundColor);
+ method public long getBackgroundColor();
+ method public long getHandleColor();
+ property public final long backgroundColor;
+ property public final long handleColor;
+ }
+
+ public final class TextSelectionColorsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.text.selection.TextSelectionColors> getLocalTextSelectionColors();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.text.selection.TextSelectionColors> LocalTextSelectionColors;
+ }
+
+}
+
diff --git a/compose/foundation/foundation/api/restricted_1.5.0-beta01.txt b/compose/foundation/foundation/api/restricted_1.5.0-beta01.txt
index 15fd553..8be51e4 100644
--- a/compose/foundation/foundation/api/restricted_1.5.0-beta01.txt
+++ b/compose/foundation/foundation/api/restricted_1.5.0-beta01.txt
@@ -6,19 +6,6 @@
method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, long color, optional androidx.compose.ui.graphics.Shape shape);
}
- public final class BasicMarqueeKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing MarqueeSpacing(float spacing);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier basicMarquee(androidx.compose.ui.Modifier, optional int iterations, optional int animationMode, optional int delayMillis, optional int initialDelayMillis, optional androidx.compose.foundation.MarqueeSpacing spacing, optional float velocity);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeDelayMillis();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static int getDefaultMarqueeIterations();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.MarqueeSpacing getDefaultMarqueeSpacing();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static float getDefaultMarqueeVelocity();
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeDelayMillis;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final int DefaultMarqueeIterations;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.foundation.MarqueeSpacing DefaultMarqueeSpacing;
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final float DefaultMarqueeVelocity;
- }
-
public final class BorderKt {
method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, androidx.compose.foundation.BorderStroke border, optional androidx.compose.ui.graphics.Shape shape);
method public static androidx.compose.ui.Modifier border(androidx.compose.ui.Modifier, float width, androidx.compose.ui.graphics.Brush brush, androidx.compose.ui.graphics.Shape shape);
@@ -39,7 +26,6 @@
}
public final class CanvasKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, String contentDescription, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
method @androidx.compose.runtime.Composable public static void Canvas(androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
}
@@ -50,8 +36,6 @@
public final class ClickableKt {
method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
}
public final class ClipScrollableContainerKt {
@@ -67,18 +51,10 @@
method @Deprecated public static androidx.compose.ui.Modifier excludeFromSystemGesture(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> exclusion);
}
- @kotlin.RequiresOptIn(message="This foundation API is experimental and is likely to change or be removed in the " + "future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalFoundationApi {
- }
-
public final class FocusableKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier focusGroup(androidx.compose.ui.Modifier);
method public static androidx.compose.ui.Modifier focusable(androidx.compose.ui.Modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
- public final class FocusedBoundsKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier onFocusedBoundsChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,kotlin.Unit> onPositioned);
- }
-
public final class HoverableKt {
method public static androidx.compose.ui.Modifier hoverable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional boolean enabled);
}
@@ -104,47 +80,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.Indication> LocalIndication;
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationApi {
- }
-
- public final class MagnifierKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier magnifier(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> sourceCenter, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Density,androidx.compose.ui.geometry.Offset> magnifierCenter, optional float zoom, optional androidx.compose.foundation.MagnifierStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.DpSize,kotlin.Unit>? onSizeChanged);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class MagnifierStyle {
- ctor @androidx.compose.foundation.ExperimentalFoundationApi public MagnifierStyle(optional long size, optional float cornerRadius, optional float elevation, optional boolean clippingEnabled, optional boolean fishEyeEnabled);
- method public boolean isSupported();
- property public final boolean isSupported;
- field public static final androidx.compose.foundation.MagnifierStyle.Companion Companion;
- }
-
- public static final class MagnifierStyle.Companion {
- method public androidx.compose.foundation.MagnifierStyle getDefault();
- method public androidx.compose.foundation.MagnifierStyle getTextDefault();
- property public final androidx.compose.foundation.MagnifierStyle Default;
- property public final androidx.compose.foundation.MagnifierStyle TextDefault;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @kotlin.jvm.JvmInline public final value class MarqueeAnimationMode {
- field public static final androidx.compose.foundation.MarqueeAnimationMode.Companion Companion;
- }
-
- public static final class MarqueeAnimationMode.Companion {
- method @androidx.compose.foundation.ExperimentalFoundationApi public int getImmediately();
- method @androidx.compose.foundation.ExperimentalFoundationApi public int getWhileFocused();
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int Immediately;
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int WhileFocused;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public fun interface MarqueeSpacing {
- method @androidx.compose.foundation.ExperimentalFoundationApi public int calculateSpacing(androidx.compose.ui.unit.Density, int contentWidth, int containerWidth);
- field public static final androidx.compose.foundation.MarqueeSpacing.Companion Companion;
- }
-
- public static final class MarqueeSpacing.Companion {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.foundation.MarqueeSpacing fractionOfContainer(float fraction);
- }
-
public enum MutatePriority {
method public static androidx.compose.foundation.MutatePriority valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
method public static androidx.compose.foundation.MutatePriority[] values();
@@ -159,32 +94,6 @@
method public suspend <T, R> Object? mutateWith(T receiver, optional androidx.compose.foundation.MutatePriority priority, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
}
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class OverscrollConfiguration {
- ctor public OverscrollConfiguration(optional long glowColor, optional androidx.compose.foundation.layout.PaddingValues drawPadding);
- method public androidx.compose.foundation.layout.PaddingValues getDrawPadding();
- method public long getGlowColor();
- property public final androidx.compose.foundation.layout.PaddingValues drawPadding;
- property public final long glowColor;
- }
-
- public final class OverscrollConfigurationKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> getLocalOverscrollConfiguration();
- property @androidx.compose.foundation.ExperimentalFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.OverscrollConfiguration> LocalOverscrollConfiguration;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface OverscrollEffect {
- method public suspend Object? applyToFling(long velocity, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Velocity,? super kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>,?> performFling, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public long applyToScroll(long delta, int source, kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,androidx.compose.ui.geometry.Offset> performScroll);
- method public androidx.compose.ui.Modifier getEffectModifier();
- method public boolean isInProgress();
- property public abstract androidx.compose.ui.Modifier effectModifier;
- property public abstract boolean isInProgress;
- }
-
- public final class OverscrollKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier overscroll(androidx.compose.ui.Modifier, androidx.compose.foundation.OverscrollEffect overscrollEffect);
- }
-
public final class ProgressSemanticsKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps);
@@ -298,13 +207,11 @@
public final class ScrollableDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior();
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public androidx.compose.foundation.OverscrollEffect overscrollEffect();
method public boolean reverseDirection(androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.foundation.gestures.Orientation orientation, boolean reverseScrolling);
field public static final androidx.compose.foundation.gestures.ScrollableDefaults INSTANCE;
}
public final class ScrollableKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, androidx.compose.foundation.OverscrollEffect? overscrollEffect, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method public static androidx.compose.ui.Modifier scrollable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.ScrollableState state, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.gestures.FlingBehavior? flingBehavior, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
@@ -347,7 +254,6 @@
public final class TransformableKt {
method public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, optional boolean lockRotationOnZoomPan, optional boolean enabled);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier transformable(androidx.compose.ui.Modifier, androidx.compose.foundation.gestures.TransformableState state, kotlin.jvm.functions.Function0<java.lang.Boolean> canPan, optional boolean lockRotationOnZoomPan, optional boolean enabled);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface TransformableState {
@@ -370,45 +276,6 @@
}
-package androidx.compose.foundation.gestures.snapping {
-
- public final class LazyGridSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
- }
-
- public final class LazyListSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
- ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onSettlingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
- }
-
- public final class SnapFlingBehaviorKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
- method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
- method public float calculateSnapStepSize(androidx.compose.ui.unit.Density);
- method public float calculateSnappingOffset(androidx.compose.ui.unit.Density, float currentVelocity);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
- method public int position(androidx.compose.ui.unit.Density, int layoutSize, int itemSize, int itemIndex);
- field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
- }
-
- public static final class SnapPositionInLayout.Companion {
- method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
- property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
- }
-
-}
-
package androidx.compose.foundation.interaction {
public interface DragInteraction extends androidx.compose.foundation.interaction.Interaction {
@@ -530,7 +397,6 @@
}
@androidx.compose.foundation.lazy.LazyScopeMarker @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
@@ -577,7 +443,6 @@
method @Deprecated public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public default void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?> contentType, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
method @Deprecated 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, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
}
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
@@ -638,7 +503,6 @@
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
method public int getCurrentLineSpan();
- property @androidx.compose.foundation.ExperimentalFoundationApi public final int currentLineSpan;
}
public final class LazyGridDslKt {
@@ -676,7 +540,6 @@
}
@androidx.compose.foundation.lazy.grid.LazyGridScopeMarker @androidx.compose.runtime.Stable public sealed interface LazyGridItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
}
@androidx.compose.foundation.lazy.grid.LazyGridScopeMarker public sealed interface LazyGridItemSpanScope {
@@ -753,110 +616,6 @@
}
-package androidx.compose.foundation.lazy.layout {
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface IntervalList<T> {
- method public void forEach(optional int fromIndex, optional int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
- method public operator androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
- method public int getSize();
- property public abstract int size;
- }
-
- public static final class IntervalList.Interval<T> {
- method public int getSize();
- method public int getStartIndex();
- method public T getValue();
- property public final int size;
- property public final int startIndex;
- property public final T value;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public abstract class LazyLayoutIntervalContent<Interval extends androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.Interval> {
- ctor public LazyLayoutIntervalContent();
- method public final Object? getContentType(int index);
- method public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> getIntervals();
- method public final int getItemCount();
- method public final Object getKey(int index);
- method public final inline <T> T withInterval(int globalIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super Interval,? extends T> block);
- property public abstract androidx.compose.foundation.lazy.layout.IntervalList<Interval> intervals;
- property public final int itemCount;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static interface LazyLayoutIntervalContent.Interval {
- method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? getKey();
- method public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> getType();
- property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object>? key;
- property public default kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Object> type;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface LazyLayoutItemProvider {
- method @androidx.compose.runtime.Composable public void Item(int index, Object key);
- method public default Object? getContentType(int index);
- method public default int getIndex(Object key);
- method public int getItemCount();
- method public default Object getKey(int index);
- property public abstract int itemCount;
- }
-
- public final class LazyLayoutItemProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
- }
-
- public final class LazyLayoutKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface LazyLayoutMeasureScope extends androidx.compose.ui.layout.MeasureScope {
- method public java.util.List<androidx.compose.ui.layout.Placeable> measure(int index, long constraints);
- method @androidx.compose.runtime.Stable public default float toDp(float);
- method @androidx.compose.runtime.Stable public default float toDp(int);
- method @androidx.compose.runtime.Stable public default float toDp(long);
- method @androidx.compose.runtime.Stable public default long toDpSize(long);
- method @androidx.compose.runtime.Stable public default long toSize(long);
- method @androidx.compose.runtime.Stable public default long toSp(float);
- method @androidx.compose.runtime.Stable public default long toSp(float);
- method @androidx.compose.runtime.Stable public default long toSp(int);
- }
-
- public final class LazyLayoutPinnableItemKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayoutPinnableItem(Object? key, int index, androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList pinnedItemList, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class LazyLayoutPinnedItemList implements kotlin.jvm.internal.markers.KMappedMarker java.util.List<androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList.PinnedItem> {
- ctor public LazyLayoutPinnedItemList();
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static sealed interface LazyLayoutPinnedItemList.PinnedItem {
- method public int getIndex();
- method public Object? getKey();
- property public abstract int index;
- property public abstract Object? key;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class LazyLayoutPrefetchState {
- ctor public LazyLayoutPrefetchState();
- method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index, long constraints);
- }
-
- public static sealed interface LazyLayoutPrefetchState.PrefetchHandle {
- method public void cancel();
- }
-
- public final class Lazy_androidKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static Object getDefaultLazyLayoutKey(int index);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class MutableIntervalList<T> implements androidx.compose.foundation.lazy.layout.IntervalList<T> {
- ctor public MutableIntervalList();
- method public void addInterval(int size, T value);
- method public void forEach(int fromIndex, int toIndex, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.IntervalList.Interval<? extends T>,kotlin.Unit> block);
- method public androidx.compose.foundation.lazy.layout.IntervalList.Interval<T> get(int index);
- method public int getSize();
- property public int size;
- }
-
-}
-
package androidx.compose.foundation.lazy.staggeredgrid {
public final class LazyStaggeredGridDslKt {
@@ -884,7 +643,6 @@
}
@androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
}
public sealed interface LazyStaggeredGridLayoutInfo {
@@ -975,109 +733,6 @@
}
-package androidx.compose.foundation.pager {
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PageSize {
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fill implements androidx.compose.foundation.pager.PageSize {
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- field public static final androidx.compose.foundation.pager.PageSize.Fill INSTANCE;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public static final class PageSize.Fixed implements androidx.compose.foundation.pager.PageSize {
- ctor public PageSize.Fixed(float pageSize);
- method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
- method public float getPageSize();
- property public final float pageSize;
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public final class PagerDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional float snapVelocityThreshold, optional float snapPositionalThreshold);
- method public androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection(androidx.compose.foundation.gestures.Orientation orientation);
- field public static final androidx.compose.foundation.pager.PagerDefaults INSTANCE;
- }
-
- public final class PagerKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PagerSnapDistance {
- method public int calculateTargetPage(int startPage, int suggestedTargetPage, float velocity, int pageSize, int pageSpacing);
- field public static final androidx.compose.foundation.pager.PagerSnapDistance.Companion Companion;
- }
-
- public static final class PagerSnapDistance.Companion {
- method public androidx.compose.foundation.pager.PagerSnapDistance atMost(int pages);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public abstract class PagerState implements androidx.compose.foundation.gestures.ScrollableState {
- ctor public PagerState(optional int initialPage, optional float initialPageOffsetFraction);
- method public final suspend Object? animateScrollToPage(int page, optional float pageOffsetFraction, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public float dispatchRawDelta(float delta);
- method public final boolean getCanScrollBackward();
- method public final boolean getCanScrollForward();
- method public final int getCurrentPage();
- method public final float getCurrentPageOffsetFraction();
- method public final int getInitialPage();
- method public final float getInitialPageOffsetFraction();
- method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
- method public final float getOffsetFractionForPage(int page);
- method public abstract int getPageCount();
- method public final int getSettledPage();
- method public final int getTargetPage();
- method public boolean isScrollInProgress();
- method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public final suspend Object? scrollToPage(int page, optional float pageOffsetFraction, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean canScrollBackward;
- property public final boolean canScrollForward;
- property public final int currentPage;
- property public final float currentPageOffsetFraction;
- property public final int initialPage;
- property public final float initialPageOffsetFraction;
- property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
- property public boolean isScrollInProgress;
- property public abstract int pageCount;
- property public final int settledPage;
- property public final int targetPage;
- }
-
- public final class PagerStateKt {
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction, kotlin.jvm.functions.Function0<java.lang.Integer> pageCount);
- }
-
-}
-
-package androidx.compose.foundation.relocation {
-
- @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
- method public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class BringIntoViewRequesterKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.relocation.BringIntoViewRequester BringIntoViewRequester();
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewRequester(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewRequester bringIntoViewRequester);
- }
-
- @androidx.compose.foundation.ExperimentalFoundationApi public interface BringIntoViewResponder {
- method @androidx.compose.foundation.ExperimentalFoundationApi public suspend Object? bringChildIntoView(kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Rect> localRect, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
- }
-
- public final class BringIntoViewResponderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.ui.Modifier bringIntoViewResponder(androidx.compose.ui.Modifier, androidx.compose.foundation.relocation.BringIntoViewResponder responder);
- }
-
-}
-
package androidx.compose.foundation.selection {
public final class SelectableGroupKt {
@@ -1221,7 +876,6 @@
public final class ClickableTextKt {
method @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void ClickableText(androidx.compose.ui.text.AnnotatedString text, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onHover, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional boolean softWrap, optional int overflow, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onClick);
}
@androidx.compose.runtime.Immutable public final class InlineTextContent {
@@ -1236,9 +890,6 @@
method public static void appendInlineContent(androidx.compose.ui.text.AnnotatedString.Builder, String id, optional String alternateText);
}
- @kotlin.RequiresOptIn(message="Internal/Unstable API for use only between foundation modules sharing " + "the same exact version, subject to change without notice.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalFoundationTextApi {
- }
-
public interface KeyboardActionScope {
method public void defaultKeyboardAction(int imeAction);
}
diff --git a/compose/material/material-icons-core/api/public_plus_experimental_1.5.0-beta01.txt b/compose/material/material-icons-core/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..b95d770
--- /dev/null
+++ b/compose/material/material-icons-core/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,1036 @@
+// Signature format: 4.0
+package androidx.compose.material.icons {
+
+ public final class Icons {
+ method public androidx.compose.material.icons.Icons.Filled getDefault();
+ property public final androidx.compose.material.icons.Icons.Filled Default;
+ field public static final androidx.compose.material.icons.Icons INSTANCE;
+ }
+
+ public static final class Icons.Filled {
+ field public static final androidx.compose.material.icons.Icons.Filled INSTANCE;
+ }
+
+ public static final class Icons.Outlined {
+ field public static final androidx.compose.material.icons.Icons.Outlined INSTANCE;
+ }
+
+ public static final class Icons.Rounded {
+ field public static final androidx.compose.material.icons.Icons.Rounded INSTANCE;
+ }
+
+ public static final class Icons.Sharp {
+ field public static final androidx.compose.material.icons.Icons.Sharp INSTANCE;
+ }
+
+ public static final class Icons.TwoTone {
+ field public static final androidx.compose.material.icons.Icons.TwoTone INSTANCE;
+ }
+
+ public final class IconsKt {
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector.Builder materialPath(androidx.compose.ui.graphics.vector.ImageVector.Builder, optional float fillAlpha, optional float strokeAlpha, optional int pathFillType, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.PathBuilder,kotlin.Unit> pathBuilder);
+ }
+
+}
+
+package androidx.compose.material.icons.filled {
+
+ public final class AccountBoxKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountBox(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class AccountCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountCircle(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class AddCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAddCircle(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class AddKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAdd(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ArrowBackKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowBack(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ArrowDropDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowDropDown(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ArrowForwardKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowForward(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class BuildKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getBuild(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class CallKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCall(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class CheckCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheckCircle(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class CheckKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheck(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ClearKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClear(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class CloseKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClose(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class CreateKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCreate(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class DateRangeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDateRange(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class DeleteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDelete(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class DoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDone(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class EditKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEdit(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class EmailKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEmail(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ExitToAppKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getExitToApp(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class FaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFace(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class FavoriteBorderKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavoriteBorder(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class FavoriteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavorite(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class HomeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getHome(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class InfoKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getInfo(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class KeyboardArrowDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowDown(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class KeyboardArrowLeftKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowLeft(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class KeyboardArrowRightKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowRight(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class KeyboardArrowUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowUp(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ListKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getList(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class LocationOnKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLocationOn(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class LockKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLock(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class MailOutlineKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMailOutline(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class MenuKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMenu(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class MoreVertKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMoreVert(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class NotificationsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getNotifications(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class PersonKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPerson(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class PhoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPhone(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class PlaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlace(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class PlayArrowKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlayArrow(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class RefreshKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getRefresh(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class SearchKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSearch(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class SendKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSend(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class SettingsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSettings(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ShareKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShare(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ShoppingCartKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShoppingCart(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class StarKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getStar(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class ThumbUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getThumbUp(androidx.compose.material.icons.Icons.Filled);
+ }
+
+ public final class WarningKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getWarning(androidx.compose.material.icons.Icons.Filled);
+ }
+
+}
+
+package androidx.compose.material.icons.outlined {
+
+ public final class AccountBoxKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountBox(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class AccountCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountCircle(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class AddCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAddCircle(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class AddKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAdd(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ArrowBackKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowBack(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ArrowDropDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowDropDown(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ArrowForwardKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowForward(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class BuildKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getBuild(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class CallKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCall(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class CheckCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheckCircle(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class CheckKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheck(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ClearKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClear(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class CloseKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClose(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class CreateKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCreate(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class DateRangeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDateRange(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class DeleteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDelete(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class DoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDone(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class EditKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEdit(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class EmailKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEmail(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ExitToAppKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getExitToApp(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class FaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFace(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class FavoriteBorderKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavoriteBorder(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class FavoriteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavorite(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class HomeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getHome(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class InfoKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getInfo(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class KeyboardArrowDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowDown(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class KeyboardArrowLeftKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowLeft(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class KeyboardArrowRightKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowRight(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class KeyboardArrowUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowUp(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ListKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getList(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class LocationOnKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLocationOn(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class LockKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLock(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class MailOutlineKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMailOutline(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class MenuKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMenu(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class MoreVertKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMoreVert(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class NotificationsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getNotifications(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class PersonKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPerson(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class PhoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPhone(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class PlaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlace(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class PlayArrowKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlayArrow(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class RefreshKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getRefresh(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class SearchKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSearch(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class SendKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSend(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class SettingsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSettings(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ShareKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShare(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ShoppingCartKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShoppingCart(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class StarKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getStar(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class ThumbUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getThumbUp(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+ public final class WarningKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getWarning(androidx.compose.material.icons.Icons.Outlined);
+ }
+
+}
+
+package androidx.compose.material.icons.rounded {
+
+ public final class AccountBoxKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountBox(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class AccountCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountCircle(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class AddCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAddCircle(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class AddKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAdd(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ArrowBackKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowBack(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ArrowDropDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowDropDown(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ArrowForwardKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowForward(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class BuildKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getBuild(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class CallKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCall(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class CheckCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheckCircle(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class CheckKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheck(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ClearKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClear(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class CloseKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClose(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class CreateKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCreate(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class DateRangeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDateRange(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class DeleteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDelete(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class DoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDone(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class EditKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEdit(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class EmailKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEmail(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ExitToAppKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getExitToApp(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class FaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFace(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class FavoriteBorderKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavoriteBorder(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class FavoriteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavorite(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class HomeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getHome(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class InfoKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getInfo(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class KeyboardArrowDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowDown(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class KeyboardArrowLeftKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowLeft(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class KeyboardArrowRightKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowRight(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class KeyboardArrowUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowUp(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ListKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getList(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class LocationOnKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLocationOn(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class LockKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLock(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class MailOutlineKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMailOutline(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class MenuKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMenu(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class MoreVertKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMoreVert(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class NotificationsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getNotifications(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class PersonKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPerson(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class PhoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPhone(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class PlaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlace(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class PlayArrowKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlayArrow(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class RefreshKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getRefresh(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class SearchKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSearch(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class SendKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSend(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class SettingsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSettings(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ShareKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShare(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ShoppingCartKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShoppingCart(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class StarKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getStar(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class ThumbUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getThumbUp(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+ public final class WarningKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getWarning(androidx.compose.material.icons.Icons.Rounded);
+ }
+
+}
+
+package androidx.compose.material.icons.sharp {
+
+ public final class AccountBoxKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountBox(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class AccountCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountCircle(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class AddCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAddCircle(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class AddKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAdd(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ArrowBackKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowBack(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ArrowDropDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowDropDown(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ArrowForwardKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowForward(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class BuildKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getBuild(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class CallKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCall(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class CheckCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheckCircle(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class CheckKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheck(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ClearKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClear(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class CloseKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClose(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class CreateKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCreate(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class DateRangeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDateRange(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class DeleteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDelete(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class DoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDone(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class EditKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEdit(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class EmailKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEmail(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ExitToAppKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getExitToApp(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class FaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFace(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class FavoriteBorderKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavoriteBorder(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class FavoriteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavorite(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class HomeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getHome(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class InfoKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getInfo(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class KeyboardArrowDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowDown(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class KeyboardArrowLeftKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowLeft(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class KeyboardArrowRightKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowRight(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class KeyboardArrowUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowUp(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ListKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getList(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class LocationOnKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLocationOn(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class LockKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLock(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class MailOutlineKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMailOutline(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class MenuKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMenu(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class MoreVertKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMoreVert(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class NotificationsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getNotifications(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class PersonKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPerson(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class PhoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPhone(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class PlaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlace(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class PlayArrowKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlayArrow(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class RefreshKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getRefresh(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class SearchKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSearch(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class SendKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSend(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class SettingsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSettings(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ShareKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShare(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ShoppingCartKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShoppingCart(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class StarKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getStar(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class ThumbUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getThumbUp(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+ public final class WarningKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getWarning(androidx.compose.material.icons.Icons.Sharp);
+ }
+
+}
+
+package androidx.compose.material.icons.twotone {
+
+ public final class AccountBoxKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountBox(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class AccountCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAccountCircle(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class AddCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAddCircle(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class AddKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getAdd(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ArrowBackKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowBack(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ArrowDropDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowDropDown(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ArrowForwardKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getArrowForward(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class BuildKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getBuild(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class CallKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCall(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class CheckCircleKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheckCircle(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class CheckKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCheck(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ClearKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClear(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class CloseKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getClose(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class CreateKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getCreate(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class DateRangeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDateRange(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class DeleteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDelete(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class DoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getDone(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class EditKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEdit(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class EmailKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getEmail(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ExitToAppKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getExitToApp(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class FaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFace(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class FavoriteBorderKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavoriteBorder(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class FavoriteKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getFavorite(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class HomeKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getHome(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class InfoKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getInfo(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class KeyboardArrowDownKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowDown(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class KeyboardArrowLeftKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowLeft(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class KeyboardArrowRightKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowRight(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class KeyboardArrowUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getKeyboardArrowUp(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ListKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getList(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class LocationOnKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLocationOn(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class LockKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getLock(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class MailOutlineKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMailOutline(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class MenuKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMenu(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class MoreVertKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getMoreVert(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class NotificationsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getNotifications(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class PersonKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPerson(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class PhoneKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPhone(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class PlaceKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlace(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class PlayArrowKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getPlayArrow(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class RefreshKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getRefresh(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class SearchKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSearch(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class SendKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSend(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class SettingsKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getSettings(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ShareKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShare(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ShoppingCartKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getShoppingCart(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class StarKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getStar(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class ThumbUpKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getThumbUp(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+ public final class WarningKt {
+ method public static androidx.compose.ui.graphics.vector.ImageVector getWarning(androidx.compose.material.icons.Icons.TwoTone);
+ }
+
+}
+
diff --git a/compose/material/material-ripple/api/public_plus_experimental_1.5.0-beta01.txt b/compose/material/material-ripple/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..6dba36f
--- /dev/null
+++ b/compose/material/material-ripple/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.compose.material.ripple {
+
+ @androidx.compose.runtime.Immutable public final class RippleAlpha {
+ ctor public RippleAlpha(float draggedAlpha, float focusedAlpha, float hoveredAlpha, float pressedAlpha);
+ method public float getDraggedAlpha();
+ method public float getFocusedAlpha();
+ method public float getHoveredAlpha();
+ method public float getPressedAlpha();
+ property public final float draggedAlpha;
+ property public final float focusedAlpha;
+ property public final float hoveredAlpha;
+ property public final float pressedAlpha;
+ }
+
+ public final class RippleKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.Indication rememberRipple(optional boolean bounded, optional float radius, optional long color);
+ }
+
+ public interface RippleTheme {
+ method @androidx.compose.runtime.Composable public long defaultColor();
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ripple.RippleAlpha rippleAlpha();
+ field public static final androidx.compose.material.ripple.RippleTheme.Companion Companion;
+ }
+
+ public static final class RippleTheme.Companion {
+ method public androidx.compose.material.ripple.RippleAlpha defaultRippleAlpha(long contentColor, boolean lightTheme);
+ method public long defaultRippleColor(long contentColor, boolean lightTheme);
+ }
+
+ public final class RippleThemeKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> getLocalRippleTheme();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ripple.RippleTheme> LocalRippleTheme;
+ }
+
+}
+
diff --git a/compose/material/material/api/1.5.0-beta01.txt b/compose/material/material/api/1.5.0-beta01.txt
index 3ce6f1ce..8935416 100644
--- a/compose/material/material/api/1.5.0-beta01.txt
+++ b/compose/material/material/api/1.5.0-beta01.txt
@@ -42,73 +42,11 @@
field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
}
- public final class BackdropScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold(kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BackdropScaffoldState scaffoldState, optional boolean gesturesEnabled, optional float peekHeight, optional float headerHeight, optional boolean persistentAppBar, optional boolean stickyFrontLayer, optional long backLayerBackgroundColor, optional long backLayerContentColor, optional androidx.compose.ui.graphics.Shape frontLayerShape, optional float frontLayerElevation, optional long frontLayerBackgroundColor, optional long frontLayerContentColor, optional long frontLayerScrimColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
- ctor public BackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- method public suspend Object? conceal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
- method public boolean isConcealed();
- method public boolean isRevealed();
- method public suspend Object? reveal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean isConcealed;
- property public final boolean isRevealed;
- property public final androidx.compose.material.SnackbarHostState snackbarHostState;
- field public static final androidx.compose.material.BackdropScaffoldState.Companion Companion;
- }
-
- public static final class BackdropScaffoldState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BackdropScaffoldState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, androidx.compose.material.SnackbarHostState snackbarHostState);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BackdropValue {
- method public static androidx.compose.material.BackdropValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BackdropValue[] values();
- enum_constant public static final androidx.compose.material.BackdropValue Concealed;
- enum_constant public static final androidx.compose.material.BackdropValue Revealed;
- }
-
public final class BadgeKt {
method @androidx.compose.runtime.Composable public static void Badge(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? content);
method @androidx.compose.runtime.Composable public static void BadgedBox(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> badge, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
- @androidx.compose.material.ExperimentalMaterialApi public final class BottomDrawerState {
- ctor @Deprecated public BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.BottomDrawerValue getCurrentValue();
- method public float getOffset();
- method public androidx.compose.material.BottomDrawerValue getTargetValue();
- method public boolean isClosed();
- method public boolean isExpanded();
- method public boolean isOpen();
- method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.BottomDrawerValue currentValue;
- property public final boolean isClosed;
- property public final boolean isExpanded;
- property public final boolean isOpen;
- property public final float offset;
- property public final androidx.compose.material.BottomDrawerValue targetValue;
- field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
- }
-
- public static final class BottomDrawerState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(androidx.compose.ui.unit.Density density, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BottomDrawerValue {
- method public static androidx.compose.material.BottomDrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BottomDrawerValue[] values();
- enum_constant public static final androidx.compose.material.BottomDrawerValue Closed;
- enum_constant public static final androidx.compose.material.BottomDrawerValue Expanded;
- enum_constant public static final androidx.compose.material.BottomDrawerValue Open;
- }
-
public final class BottomNavigationDefaults {
method public float getElevation();
property public final float Elevation;
@@ -128,54 +66,6 @@
field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
}
- public final class BottomSheetScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional int floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, 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 drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomSheetState BottomSheetScaffoldState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public static androidx.compose.material.BottomSheetState BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.BottomSheetState bottomSheetState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetScaffoldState {
- ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
- method public androidx.compose.material.BottomSheetState getBottomSheetState();
- method public androidx.compose.material.DrawerState getDrawerState();
- method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
- property public final androidx.compose.material.BottomSheetState bottomSheetState;
- property public final androidx.compose.material.DrawerState drawerState;
- property public final androidx.compose.material.SnackbarHostState snackbarHostState;
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetState {
- ctor @Deprecated public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
- method public suspend Object? collapse(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.BottomSheetValue getCurrentValue();
- method @Deprecated public float getOffset();
- method public float getProgress();
- method public boolean isCollapsed();
- method public boolean isExpanded();
- method public float requireOffset();
- property public final androidx.compose.material.BottomSheetValue currentValue;
- property public final boolean isCollapsed;
- property public final boolean isExpanded;
- property @Deprecated public final float offset;
- property public final float progress;
- field public static final androidx.compose.material.BottomSheetState.Companion Companion;
- }
-
- public static final class BottomSheetState.Companion {
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange, androidx.compose.ui.unit.Density density);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
- method public static androidx.compose.material.BottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BottomSheetValue[] values();
- enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
- enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
- }
-
@androidx.compose.runtime.Stable public interface ButtonColors {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
@@ -219,8 +109,6 @@
public final class CardKt {
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
@androidx.compose.runtime.Stable public interface CheckboxColors {
@@ -239,38 +127,6 @@
method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.CheckboxColors colors);
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ChipColors {
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconContentColor(boolean enabled);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ChipDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors chipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors filterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
- method public float getLeadingIconSize();
- method public float getMinHeight();
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
- method public float getOutlinedBorderSize();
- method public float getSelectedIconSize();
- method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors outlinedChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors outlinedFilterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
- property public final float LeadingIconSize;
- property public final float MinHeight;
- property public final float OutlinedBorderSize;
- property public final float SelectedIconSize;
- property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
- field public static final float ContentOpacity = 0.87f;
- field public static final androidx.compose.material.ChipDefaults INSTANCE;
- field public static final float LeadingIconOpacity = 0.54f;
- field public static final float OutlinedBorderOpacity = 0.12f;
- }
-
- public final class ChipKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Chip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.SelectableChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? selectedIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- }
-
@androidx.compose.runtime.Stable public final class Colors {
ctor public Colors(long primary, long primaryVariant, long secondary, long secondaryVariant, long background, long surface, long error, long onPrimary, long onSecondary, long onBackground, long onSurface, long onError, boolean isLight);
method public androidx.compose.material.Colors copy(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError, optional boolean isLight);
@@ -337,20 +193,6 @@
enum_constant public static final androidx.compose.material.DismissDirection StartToEnd;
}
- @androidx.compose.material.ExperimentalMaterialApi public final class DismissState extends androidx.compose.material.SwipeableState<androidx.compose.material.DismissValue> {
- ctor public DismissState(androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
- method public suspend Object? dismiss(androidx.compose.material.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.DismissDirection? getDismissDirection();
- method public boolean isDismissed(androidx.compose.material.DismissDirection direction);
- method public suspend Object? reset(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.DismissDirection? dismissDirection;
- field public static final androidx.compose.material.DismissState.Companion Companion;
- }
-
- public static final class DismissState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.DismissState,androidx.compose.material.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
- }
-
public enum DismissValue {
method public static androidx.compose.material.DismissValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
method public static androidx.compose.material.DismissValue[] values();
@@ -373,20 +215,14 @@
}
public final class DrawerKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomDrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomDrawerState BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, androidx.compose.ui.unit.Density density, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
method @androidx.compose.runtime.Composable public static void ModalDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
}
@androidx.compose.runtime.Stable public final class DrawerState {
ctor public DrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public suspend Object? animateTo(androidx.compose.material.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public androidx.compose.material.DrawerValue getCurrentValue();
- method @androidx.compose.material.ExperimentalMaterialApi public float getOffset();
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.material.DrawerValue getTargetValue();
method public boolean isAnimationRunning();
method public boolean isClosed();
method public boolean isOpen();
@@ -396,8 +232,6 @@
property public final boolean isAnimationRunning;
property public final boolean isClosed;
property public final boolean isOpen;
- property @androidx.compose.material.ExperimentalMaterialApi public final float offset;
- property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.DrawerValue targetValue;
field public static final androidx.compose.material.DrawerState.Companion Companion;
}
@@ -423,25 +257,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> LocalElevationOverlay;
}
- @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterialApi {
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface ExposedDropdownMenuBoxScope {
- method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
- method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ExposedDropdownMenuDefaults {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TrailingIcon(boolean expanded, optional kotlin.jvm.functions.Function0<kotlin.Unit> onIconClick);
- method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- field public static final androidx.compose.material.ExposedDropdownMenuDefaults INSTANCE;
- }
-
- public final class ExposedDropdownMenuKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ExposedDropdownMenuBox(boolean expanded, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onExpandedChange, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material.ExposedDropdownMenuBoxScope,kotlin.Unit> content);
- }
-
@kotlin.jvm.JvmInline public final value class FabPosition {
field public static final androidx.compose.material.FabPosition.Companion Companion;
}
@@ -453,12 +268,6 @@
property public final int End;
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FixedThreshold implements androidx.compose.material.ThresholdConfig {
- ctor public FixedThreshold(float offset);
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
- }
-
public final class FloatingActionButtonDefaults {
method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation);
method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float hoveredElevation, optional float focusedElevation);
@@ -474,12 +283,6 @@
method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.material.FloatingActionButtonElevation elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FractionalThreshold implements androidx.compose.material.ThresholdConfig {
- ctor public FractionalThreshold(float fraction);
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.compose.material.FractionalThreshold copy(float fraction);
- }
-
public final class IconButtonKt {
method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -492,15 +295,7 @@
}
public final class InteractiveComponentSizeKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumInteractiveComponentEnforcement();
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumTouchTargetEnforcement();
method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
- property @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumInteractiveComponentEnforcement;
- property @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumTouchTargetEnforcement;
- }
-
- public final class ListItemKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ListItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? secondaryText, optional boolean singleLineSecondaryText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailing, kotlin.jvm.functions.Function0<kotlin.Unit> text);
}
public final class MaterialTheme {
@@ -531,43 +326,6 @@
field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
}
- public final class ModalBottomSheetKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ModalBottomSheetState sheetState, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHalfExpanded);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState {
- ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional boolean isSkipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method public androidx.compose.material.ModalBottomSheetValue getCurrentValue();
- method public androidx.compose.material.ModalBottomSheetValue getTargetValue();
- method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public boolean isVisible();
- method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.ModalBottomSheetValue currentValue;
- property public final boolean isVisible;
- property public final androidx.compose.material.ModalBottomSheetValue targetValue;
- field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
- }
-
- public static final class ModalBottomSheetState.Companion {
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded);
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded, androidx.compose.ui.unit.Density density);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
- method public static androidx.compose.material.ModalBottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.ModalBottomSheetValue[] values();
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
- }
-
public final class NavigationRailDefaults {
method public float getElevation();
property public final float Elevation;
@@ -643,12 +401,6 @@
property public final androidx.compose.material.SnackbarHostState snackbarHostState;
}
- @androidx.compose.material.ExperimentalMaterialApi public interface SelectableChipColors {
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled, boolean selected);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean selected);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean selected);
- }
-
@androidx.compose.runtime.Immutable public final class Shapes {
ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
method public androidx.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
@@ -677,7 +429,6 @@
}
public final class SliderKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
}
@@ -733,25 +484,6 @@
public final class SurfaceKt {
method @androidx.compose.runtime.Composable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class SwipeProgress<T> {
- ctor public SwipeProgress(T from, T to, float fraction);
- method public float getFraction();
- method public T getFrom();
- method public T getTo();
- property public final float fraction;
- property public final T from;
- property public final T to;
- }
-
- public final class SwipeToDismissKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material.DismissState state, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material.DismissDirection> directions, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissDirection,? extends androidx.compose.material.ThresholdConfig> dismissThresholds, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
}
public final class SwipeableDefaults {
@@ -765,38 +497,6 @@
field public static final float StiffResistanceFactor = 20.0f;
}
- public final class SwipeableKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material.SwipeableState<T> rememberSwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi public static <T> androidx.compose.ui.Modifier swipeable(androidx.compose.ui.Modifier, androidx.compose.material.SwipeableState<T> state, java.util.Map<java.lang.Float,? extends T> anchors, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends androidx.compose.material.ThresholdConfig> thresholds, optional androidx.compose.material.ResistanceConfig? resistance, optional float velocityThreshold);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public class SwipeableState<T> {
- ctor public SwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? animateTo(T targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public final T getCurrentValue();
- method public final float getDirection();
- method public final androidx.compose.runtime.State<java.lang.Float> getOffset();
- method public final androidx.compose.runtime.State<java.lang.Float> getOverflow();
- method public final androidx.compose.material.SwipeProgress<T> getProgress();
- method public final T getTargetValue();
- method public final boolean isAnimationRunning();
- method public final float performDrag(float delta);
- method public final suspend Object? performFling(float velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final T currentValue;
- property @androidx.compose.material.ExperimentalMaterialApi public final float direction;
- property public final boolean isAnimationRunning;
- property public final androidx.compose.runtime.State<java.lang.Float> offset;
- property public final androidx.compose.runtime.State<java.lang.Float> overflow;
- property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.SwipeProgress<T> progress;
- property @androidx.compose.material.ExperimentalMaterialApi public final T targetValue;
- field public static final androidx.compose.material.SwipeableState.Companion Companion;
- }
-
- public static final class SwipeableState.Companion {
- method public <T> androidx.compose.runtime.saveable.Saver<androidx.compose.material.SwipeableState<T>,T> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- }
-
@androidx.compose.runtime.Stable public interface SwitchColors {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
@@ -856,27 +556,15 @@
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError);
}
- @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface TextFieldColorsWithIcons extends androidx.compose.material.TextFieldColors {
- method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
- method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
- }
-
@androidx.compose.runtime.Immutable public final class TextFieldDefaults {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
method public float getUnfocusedBorderThickness();
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.ui.Modifier indicatorLine(androidx.compose.ui.Modifier, boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional float focusedIndicatorLineThickness, optional float unfocusedIndicatorLineThickness);
method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
property public final float FocusedBorderThickness;
property public final float MinHeight;
property public final float MinWidth;
@@ -906,10 +594,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- }
-
@androidx.compose.runtime.Immutable public final class Typography {
ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
method public androidx.compose.material.Typography copy(optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
@@ -943,37 +627,3 @@
}
-package androidx.compose.material.pullrefresh {
-
- @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshDefaults {
- method public float getRefreshThreshold();
- method public float getRefreshingOffset();
- property public final float RefreshThreshold;
- property public final float RefreshingOffset;
- field public static final androidx.compose.material.pullrefresh.PullRefreshDefaults INSTANCE;
- }
-
- public final class PullRefreshIndicatorKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void PullRefreshIndicator(boolean refreshing, androidx.compose.material.pullrefresh.PullRefreshState state, optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional boolean scale);
- }
-
- public final class PullRefreshIndicatorTransformKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefreshIndicatorTransform(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean scale);
- }
-
- public final class PullRefreshKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean enabled);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> onPull, kotlin.jvm.functions.Function2<? super java.lang.Float,? super kotlin.coroutines.Continuation<? super java.lang.Float>,?> onRelease, optional boolean enabled);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshState {
- method public float getProgress();
- property public final float progress;
- }
-
- public final class PullRefreshStateKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.pullrefresh.PullRefreshState rememberPullRefreshState(boolean refreshing, kotlin.jvm.functions.Function0<kotlin.Unit> onRefresh, optional float refreshThreshold, optional float refreshingOffset);
- }
-
-}
-
diff --git a/compose/material/material/api/public_plus_experimental_1.5.0-beta01.txt b/compose/material/material/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..3ce6f1ce
--- /dev/null
+++ b/compose/material/material/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,979 @@
+// Signature format: 4.0
+package androidx.compose.material {
+
+ public final class AndroidAlertDialog_androidKt {
+ method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> buttons, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.ui.window.DialogProperties properties);
+ method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.ui.window.DialogProperties properties);
+ }
+
+ public final class AndroidMenu_androidKt {
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ }
+
+ public final class AppBarDefaults {
+ method public float getBottomAppBarElevation();
+ method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+ method public float getTopAppBarElevation();
+ property public final float BottomAppBarElevation;
+ property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+ property public final float TopAppBarElevation;
+ field public static final androidx.compose.material.AppBarDefaults INSTANCE;
+ }
+
+ public final class AppBarKt {
+ method @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional androidx.compose.ui.graphics.Shape? cutoutShape, optional float elevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void TopAppBar(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional long backgroundColor, optional long contentColor, optional float elevation);
+ }
+
+ public final class BackdropScaffoldDefaults {
+ method public float getFrontLayerElevation();
+ method @androidx.compose.runtime.Composable public long getFrontLayerScrimColor();
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFrontLayerShape();
+ method public float getHeaderHeight();
+ method public float getPeekHeight();
+ property public final float FrontLayerElevation;
+ property public final float HeaderHeight;
+ property public final float PeekHeight;
+ property @androidx.compose.runtime.Composable public final long frontLayerScrimColor;
+ property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape frontLayerShape;
+ field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
+ }
+
+ public final class BackdropScaffoldKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold(kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BackdropScaffoldState scaffoldState, optional boolean gesturesEnabled, optional float peekHeight, optional float headerHeight, optional boolean persistentAppBar, optional boolean stickyFrontLayer, optional long backLayerBackgroundColor, optional long backLayerContentColor, optional androidx.compose.ui.graphics.Shape frontLayerShape, optional float frontLayerElevation, optional long frontLayerBackgroundColor, optional long frontLayerContentColor, optional long frontLayerScrimColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
+ ctor public BackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public suspend Object? conceal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ method public boolean isConcealed();
+ method public boolean isRevealed();
+ method public suspend Object? reveal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final boolean isConcealed;
+ property public final boolean isRevealed;
+ property public final androidx.compose.material.SnackbarHostState snackbarHostState;
+ field public static final androidx.compose.material.BackdropScaffoldState.Companion Companion;
+ }
+
+ public static final class BackdropScaffoldState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BackdropScaffoldState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, androidx.compose.material.SnackbarHostState snackbarHostState);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BackdropValue {
+ method public static androidx.compose.material.BackdropValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.BackdropValue[] values();
+ enum_constant public static final androidx.compose.material.BackdropValue Concealed;
+ enum_constant public static final androidx.compose.material.BackdropValue Revealed;
+ }
+
+ public final class BadgeKt {
+ method @androidx.compose.runtime.Composable public static void Badge(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? content);
+ method @androidx.compose.runtime.Composable public static void BadgedBox(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> badge, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class BottomDrawerState {
+ ctor @Deprecated public BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
+ method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material.BottomDrawerValue getCurrentValue();
+ method public float getOffset();
+ method public androidx.compose.material.BottomDrawerValue getTargetValue();
+ method public boolean isClosed();
+ method public boolean isExpanded();
+ method public boolean isOpen();
+ method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final androidx.compose.material.BottomDrawerValue currentValue;
+ property public final boolean isClosed;
+ property public final boolean isExpanded;
+ property public final boolean isOpen;
+ property public final float offset;
+ property public final androidx.compose.material.BottomDrawerValue targetValue;
+ field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
+ }
+
+ public static final class BottomDrawerState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(androidx.compose.ui.unit.Density density, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BottomDrawerValue {
+ method public static androidx.compose.material.BottomDrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.BottomDrawerValue[] values();
+ enum_constant public static final androidx.compose.material.BottomDrawerValue Closed;
+ enum_constant public static final androidx.compose.material.BottomDrawerValue Expanded;
+ enum_constant public static final androidx.compose.material.BottomDrawerValue Open;
+ }
+
+ public final class BottomNavigationDefaults {
+ method public float getElevation();
+ property public final float Elevation;
+ field public static final androidx.compose.material.BottomNavigationDefaults INSTANCE;
+ }
+
+ public final class BottomNavigationKt {
+ method @androidx.compose.runtime.Composable public static void BottomNavigation(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void BottomNavigationItem(androidx.compose.foundation.layout.RowScope, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+ }
+
+ public final class BottomSheetScaffoldDefaults {
+ method public float getSheetElevation();
+ method public float getSheetPeekHeight();
+ property public final float SheetElevation;
+ property public final float SheetPeekHeight;
+ field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
+ }
+
+ public final class BottomSheetScaffoldKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional int floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, 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 drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomSheetState BottomSheetScaffoldState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public static androidx.compose.material.BottomSheetState BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.BottomSheetState bottomSheetState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetScaffoldState {
+ ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public androidx.compose.material.BottomSheetState getBottomSheetState();
+ method public androidx.compose.material.DrawerState getDrawerState();
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ property public final androidx.compose.material.BottomSheetState bottomSheetState;
+ property public final androidx.compose.material.DrawerState drawerState;
+ property public final androidx.compose.material.SnackbarHostState snackbarHostState;
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetState {
+ ctor @Deprecated public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
+ method public suspend Object? collapse(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material.BottomSheetValue getCurrentValue();
+ method @Deprecated public float getOffset();
+ method public float getProgress();
+ method public boolean isCollapsed();
+ method public boolean isExpanded();
+ method public float requireOffset();
+ property public final androidx.compose.material.BottomSheetValue currentValue;
+ property public final boolean isCollapsed;
+ property public final boolean isExpanded;
+ property @Deprecated public final float offset;
+ property public final float progress;
+ field public static final androidx.compose.material.BottomSheetState.Companion Companion;
+ }
+
+ public static final class BottomSheetState.Companion {
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange, androidx.compose.ui.unit.Density density);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
+ method public static androidx.compose.material.BottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.BottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
+ enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
+ }
+
+ @androidx.compose.runtime.Stable public interface ButtonColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
+ }
+
+ public final class ButtonDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors buttonColors(optional long backgroundColor, optional long contentColor, optional long disabledBackgroundColor, optional long disabledContentColor);
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float disabledElevation, optional float hoveredElevation, optional float focusedElevation);
+ method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+ method public float getIconSize();
+ method public float getIconSpacing();
+ method public float getMinHeight();
+ method public float getMinWidth();
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
+ method public float getOutlinedBorderSize();
+ method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors outlinedButtonColors(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ButtonColors textButtonColors(optional long backgroundColor, optional long contentColor, optional long disabledContentColor);
+ property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+ property public final float IconSize;
+ property public final float IconSpacing;
+ property public final float MinHeight;
+ property public final float MinWidth;
+ property public final float OutlinedBorderSize;
+ property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+ property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
+ field public static final androidx.compose.material.ButtonDefaults INSTANCE;
+ field public static final float OutlinedBorderOpacity = 0.12f;
+ }
+
+ @androidx.compose.runtime.Stable public interface ButtonElevation {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(boolean enabled, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ }
+
+ public final class ButtonKt {
+ method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.ButtonElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ButtonColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.ButtonElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ButtonColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.ButtonElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ButtonColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ }
+
+ public final class CardKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @androidx.compose.runtime.Stable public interface CheckboxColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> boxColor(boolean enabled, androidx.compose.ui.state.ToggleableState state);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> checkmarkColor(androidx.compose.ui.state.ToggleableState state);
+ }
+
+ public final class CheckboxDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledColor, optional long disabledIndeterminateColor);
+ field public static final androidx.compose.material.CheckboxDefaults INSTANCE;
+ }
+
+ public final class CheckboxKt {
+ method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.CheckboxColors colors);
+ method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.CheckboxColors colors);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ChipColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconContentColor(boolean enabled);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ChipDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors chipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors filterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
+ method public float getLeadingIconSize();
+ method public float getMinHeight();
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
+ method public float getOutlinedBorderSize();
+ method public float getSelectedIconSize();
+ method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors outlinedChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors outlinedFilterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
+ property public final float LeadingIconSize;
+ property public final float MinHeight;
+ property public final float OutlinedBorderSize;
+ property public final float SelectedIconSize;
+ property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
+ field public static final float ContentOpacity = 0.87f;
+ field public static final androidx.compose.material.ChipDefaults INSTANCE;
+ field public static final float LeadingIconOpacity = 0.54f;
+ field public static final float OutlinedBorderOpacity = 0.12f;
+ }
+
+ public final class ChipKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Chip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.SelectableChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? selectedIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ }
+
+ @androidx.compose.runtime.Stable public final class Colors {
+ ctor public Colors(long primary, long primaryVariant, long secondary, long secondaryVariant, long background, long surface, long error, long onPrimary, long onSecondary, long onBackground, long onSurface, long onError, boolean isLight);
+ method public androidx.compose.material.Colors copy(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError, optional boolean isLight);
+ method public long getBackground();
+ method public long getError();
+ method public long getOnBackground();
+ method public long getOnError();
+ method public long getOnPrimary();
+ method public long getOnSecondary();
+ method public long getOnSurface();
+ method public long getPrimary();
+ method public long getPrimaryVariant();
+ method public long getSecondary();
+ method public long getSecondaryVariant();
+ method public long getSurface();
+ method public boolean isLight();
+ property public final long background;
+ property public final long error;
+ property public final boolean isLight;
+ property public final long onBackground;
+ property public final long onError;
+ property public final long onPrimary;
+ property public final long onSecondary;
+ property public final long onSurface;
+ property public final long primary;
+ property public final long primaryVariant;
+ property public final long secondary;
+ property public final long secondaryVariant;
+ property public final long surface;
+ }
+
+ public final class ColorsKt {
+ method public static long contentColorFor(androidx.compose.material.Colors, long backgroundColor);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+ method public static androidx.compose.material.Colors darkColors(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
+ method public static long getPrimarySurface(androidx.compose.material.Colors);
+ method public static androidx.compose.material.Colors lightColors(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError);
+ }
+
+ public final class ContentAlpha {
+ method @androidx.compose.runtime.Composable public float getDisabled();
+ method @androidx.compose.runtime.Composable public float getHigh();
+ method @androidx.compose.runtime.Composable public float getMedium();
+ property @androidx.compose.runtime.Composable public final float disabled;
+ property @androidx.compose.runtime.Composable public final float high;
+ property @androidx.compose.runtime.Composable public final float medium;
+ field public static final androidx.compose.material.ContentAlpha INSTANCE;
+ }
+
+ public final class ContentAlphaKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> getLocalContentAlpha();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> LocalContentAlpha;
+ }
+
+ public final class ContentColorKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+ }
+
+ public enum DismissDirection {
+ method public static androidx.compose.material.DismissDirection valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.DismissDirection[] values();
+ enum_constant public static final androidx.compose.material.DismissDirection EndToStart;
+ enum_constant public static final androidx.compose.material.DismissDirection StartToEnd;
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class DismissState extends androidx.compose.material.SwipeableState<androidx.compose.material.DismissValue> {
+ ctor public DismissState(androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
+ method public suspend Object? dismiss(androidx.compose.material.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material.DismissDirection? getDismissDirection();
+ method public boolean isDismissed(androidx.compose.material.DismissDirection direction);
+ method public suspend Object? reset(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final androidx.compose.material.DismissDirection? dismissDirection;
+ field public static final androidx.compose.material.DismissState.Companion Companion;
+ }
+
+ public static final class DismissState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.DismissState,androidx.compose.material.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ public enum DismissValue {
+ method public static androidx.compose.material.DismissValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.DismissValue[] values();
+ enum_constant public static final androidx.compose.material.DismissValue Default;
+ enum_constant public static final androidx.compose.material.DismissValue DismissedToEnd;
+ enum_constant public static final androidx.compose.material.DismissValue DismissedToStart;
+ }
+
+ public final class DividerKt {
+ method @androidx.compose.runtime.Composable public static void Divider(optional androidx.compose.ui.Modifier modifier, optional long color, optional float thickness, optional float startIndent);
+ }
+
+ public final class DrawerDefaults {
+ method public float getElevation();
+ method @androidx.compose.runtime.Composable public long getScrimColor();
+ property public final float Elevation;
+ property @androidx.compose.runtime.Composable public final long scrimColor;
+ field public static final androidx.compose.material.DrawerDefaults INSTANCE;
+ field public static final float ScrimOpacity = 0.32f;
+ }
+
+ public final class DrawerKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomDrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomDrawerState BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, androidx.compose.ui.unit.Density density, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.runtime.Composable public static void ModalDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.runtime.Stable public final class DrawerState {
+ ctor public DrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public suspend Object? animateTo(androidx.compose.material.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material.DrawerValue getCurrentValue();
+ method @androidx.compose.material.ExperimentalMaterialApi public float getOffset();
+ method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.material.DrawerValue getTargetValue();
+ method public boolean isAnimationRunning();
+ method public boolean isClosed();
+ method public boolean isOpen();
+ method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? snapTo(androidx.compose.material.DrawerValue targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final androidx.compose.material.DrawerValue currentValue;
+ property public final boolean isAnimationRunning;
+ property public final boolean isClosed;
+ property public final boolean isOpen;
+ property @androidx.compose.material.ExperimentalMaterialApi public final float offset;
+ property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.DrawerValue targetValue;
+ field public static final androidx.compose.material.DrawerState.Companion Companion;
+ }
+
+ public static final class DrawerState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.DrawerState,androidx.compose.material.DrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ public enum DrawerValue {
+ method public static androidx.compose.material.DrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.DrawerValue[] values();
+ enum_constant public static final androidx.compose.material.DrawerValue Closed;
+ enum_constant public static final androidx.compose.material.DrawerValue Open;
+ }
+
+ public interface ElevationOverlay {
+ method @androidx.compose.runtime.Composable public long apply(long color, float elevation);
+ }
+
+ public final class ElevationOverlayKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteElevation();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> getLocalElevationOverlay();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteElevation;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> LocalElevationOverlay;
+ }
+
+ @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterialApi {
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface ExposedDropdownMenuBoxScope {
+ method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ExposedDropdownMenuDefaults {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TrailingIcon(boolean expanded, optional kotlin.jvm.functions.Function0<kotlin.Unit> onIconClick);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+ field public static final androidx.compose.material.ExposedDropdownMenuDefaults INSTANCE;
+ }
+
+ public final class ExposedDropdownMenuKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ExposedDropdownMenuBox(boolean expanded, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onExpandedChange, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material.ExposedDropdownMenuBoxScope,kotlin.Unit> content);
+ }
+
+ @kotlin.jvm.JvmInline public final value class FabPosition {
+ field public static final androidx.compose.material.FabPosition.Companion Companion;
+ }
+
+ public static final class FabPosition.Companion {
+ method public int getCenter();
+ method public int getEnd();
+ property public final int Center;
+ property public final int End;
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FixedThreshold implements androidx.compose.material.ThresholdConfig {
+ ctor public FixedThreshold(float offset);
+ method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
+ method public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
+ }
+
+ public final class FloatingActionButtonDefaults {
+ method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float hoveredElevation, optional float focusedElevation);
+ field public static final androidx.compose.material.FloatingActionButtonDefaults INSTANCE;
+ }
+
+ @androidx.compose.runtime.Stable public interface FloatingActionButtonElevation {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> elevation(androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ }
+
+ public final class FloatingActionButtonKt {
+ method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.material.FloatingActionButtonElevation elevation);
+ method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.material.FloatingActionButtonElevation elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FractionalThreshold implements androidx.compose.material.ThresholdConfig {
+ ctor public FractionalThreshold(float fraction);
+ method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
+ method public androidx.compose.material.FractionalThreshold copy(float fraction);
+ }
+
+ public final class IconButtonKt {
+ method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class IconKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+ method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+ }
+
+ public final class InteractiveComponentSizeKt {
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumInteractiveComponentEnforcement();
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumTouchTargetEnforcement();
+ method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+ property @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumInteractiveComponentEnforcement;
+ property @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumTouchTargetEnforcement;
+ }
+
+ public final class ListItemKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ListItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? secondaryText, optional boolean singleLineSecondaryText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailing, kotlin.jvm.functions.Function0<kotlin.Unit> text);
+ }
+
+ public final class MaterialTheme {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Colors getColors();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Shapes getShapes();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material.Typography getTypography();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Colors colors;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Shapes shapes;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material.Typography typography;
+ field public static final androidx.compose.material.MaterialTheme INSTANCE;
+ }
+
+ public final class MaterialThemeKt {
+ method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material.Colors colors, optional androidx.compose.material.Typography typography, optional androidx.compose.material.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class MenuDefaults {
+ method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+ property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+ field public static final androidx.compose.material.MenuDefaults INSTANCE;
+ }
+
+ public final class ModalBottomSheetDefaults {
+ method public float getElevation();
+ method @androidx.compose.runtime.Composable public long getScrimColor();
+ property public final float Elevation;
+ property @androidx.compose.runtime.Composable public final long scrimColor;
+ field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
+ }
+
+ public final class ModalBottomSheetKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ModalBottomSheetState sheetState, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHalfExpanded);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState {
+ ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional boolean isSkipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method public androidx.compose.material.ModalBottomSheetValue getCurrentValue();
+ method public androidx.compose.material.ModalBottomSheetValue getTargetValue();
+ method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean isVisible();
+ method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final androidx.compose.material.ModalBottomSheetValue currentValue;
+ property public final boolean isVisible;
+ property public final androidx.compose.material.ModalBottomSheetValue targetValue;
+ field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
+ }
+
+ public static final class ModalBottomSheetState.Companion {
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
+ method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded);
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded, androidx.compose.ui.unit.Density density);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
+ method public static androidx.compose.material.ModalBottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.ModalBottomSheetValue[] values();
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
+ enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
+ }
+
+ public final class NavigationRailDefaults {
+ method public float getElevation();
+ property public final float Elevation;
+ field public static final androidx.compose.material.NavigationRailDefaults INSTANCE;
+ }
+
+ public final class NavigationRailKt {
+ method @androidx.compose.runtime.Composable public static void NavigationRail(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float elevation, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? header, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationRailItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+ }
+
+ public final class OutlinedTextFieldKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+ method public float getStrokeWidth();
+ property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+ property public final float StrokeWidth;
+ field public static final androidx.compose.material.ProgressIndicatorDefaults INSTANCE;
+ field public static final float IndicatorBackgroundOpacity = 0.24f;
+ }
+
+ public final class ProgressIndicatorKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
+ method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
+ method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
+ method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
+ }
+
+ @androidx.compose.runtime.Stable public interface RadioButtonColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> radioColor(boolean enabled, boolean selected);
+ }
+
+ public final class RadioButtonDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor, optional long disabledColor);
+ field public static final androidx.compose.material.RadioButtonDefaults INSTANCE;
+ }
+
+ public final class RadioButtonKt {
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.RadioButtonColors colors);
+ }
+
+ @androidx.compose.runtime.Immutable public final class ResistanceConfig {
+ ctor public ResistanceConfig(float basis, optional float factorAtMin, optional float factorAtMax);
+ method public float computeResistance(float overflow);
+ method public float getBasis();
+ method public float getFactorAtMax();
+ method public float getFactorAtMin();
+ property public final float basis;
+ property public final float factorAtMax;
+ property public final float factorAtMin;
+ }
+
+ public final class ScaffoldKt {
+ method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional boolean isFloatingActionButtonDocked, 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 drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static androidx.compose.material.ScaffoldState rememberScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
+ }
+
+ @androidx.compose.runtime.Stable public final class ScaffoldState {
+ ctor public ScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.SnackbarHostState snackbarHostState);
+ method public androidx.compose.material.DrawerState getDrawerState();
+ method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
+ property public final androidx.compose.material.DrawerState drawerState;
+ property public final androidx.compose.material.SnackbarHostState snackbarHostState;
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public interface SelectableChipColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled, boolean selected);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean selected);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean selected);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Shapes {
+ ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
+ method public androidx.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
+ method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+ method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+ method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+ property public final androidx.compose.foundation.shape.CornerBasedShape large;
+ property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+ property public final androidx.compose.foundation.shape.CornerBasedShape small;
+ }
+
+ @androidx.compose.runtime.Stable public interface SliderColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> tickColor(boolean enabled, boolean active);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean active);
+ }
+
+ public final class SliderDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.SliderColors colors(optional long thumbColor, optional long disabledThumbColor, optional long activeTrackColor, optional long inactiveTrackColor, optional long disabledActiveTrackColor, optional long disabledInactiveTrackColor, optional long activeTickColor, optional long inactiveTickColor, optional long disabledActiveTickColor, optional long disabledInactiveTickColor);
+ field public static final float DisabledActiveTrackAlpha = 0.32f;
+ field public static final float DisabledInactiveTrackAlpha = 0.12f;
+ field public static final float DisabledTickAlpha = 0.12f;
+ field public static final androidx.compose.material.SliderDefaults INSTANCE;
+ field public static final float InactiveTrackAlpha = 0.24f;
+ field public static final float TickAlpha = 0.54f;
+ }
+
+ public final class SliderKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
+ method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
+ }
+
+ public interface SnackbarData {
+ method public void dismiss();
+ method public String? getActionLabel();
+ method public androidx.compose.material.SnackbarDuration getDuration();
+ method public String getMessage();
+ method public void performAction();
+ property public abstract String? actionLabel;
+ property public abstract androidx.compose.material.SnackbarDuration duration;
+ property public abstract String message;
+ }
+
+ public final class SnackbarDefaults {
+ method @androidx.compose.runtime.Composable public long getBackgroundColor();
+ method @androidx.compose.runtime.Composable public long getPrimaryActionColor();
+ property @androidx.compose.runtime.Composable public final long backgroundColor;
+ property @androidx.compose.runtime.Composable public final long primaryActionColor;
+ field public static final androidx.compose.material.SnackbarDefaults INSTANCE;
+ }
+
+ public enum SnackbarDuration {
+ method public static androidx.compose.material.SnackbarDuration valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.SnackbarDuration[] values();
+ enum_constant public static final androidx.compose.material.SnackbarDuration Indefinite;
+ enum_constant public static final androidx.compose.material.SnackbarDuration Long;
+ enum_constant public static final androidx.compose.material.SnackbarDuration Short;
+ }
+
+ public final class SnackbarHostKt {
+ method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarData,kotlin.Unit> snackbar);
+ }
+
+ @androidx.compose.runtime.Stable public final class SnackbarHostState {
+ ctor public SnackbarHostState();
+ method public androidx.compose.material.SnackbarData? getCurrentSnackbarData();
+ method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional androidx.compose.material.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material.SnackbarResult>);
+ property public final androidx.compose.material.SnackbarData? currentSnackbarData;
+ }
+
+ public final class SnackbarKt {
+ method @androidx.compose.runtime.Composable public static void Snackbar(androidx.compose.material.SnackbarData snackbarData, optional androidx.compose.ui.Modifier modifier, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional long actionColor, optional float elevation);
+ method @androidx.compose.runtime.Composable public static void Snackbar(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public enum SnackbarResult {
+ method public static androidx.compose.material.SnackbarResult valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material.SnackbarResult[] values();
+ enum_constant public static final androidx.compose.material.SnackbarResult ActionPerformed;
+ enum_constant public static final androidx.compose.material.SnackbarResult Dismissed;
+ }
+
+ public final class SurfaceKt {
+ method @androidx.compose.runtime.Composable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class SwipeProgress<T> {
+ ctor public SwipeProgress(T from, T to, float fraction);
+ method public float getFraction();
+ method public T getFrom();
+ method public T getTo();
+ property public final float fraction;
+ property public final T from;
+ property public final T to;
+ }
+
+ public final class SwipeToDismissKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material.DismissState state, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material.DismissDirection> directions, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissDirection,? extends androidx.compose.material.ThresholdConfig> dismissThresholds, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
+ }
+
+ public final class SwipeableDefaults {
+ method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
+ method public float getVelocityThreshold();
+ method public androidx.compose.material.ResistanceConfig? resistanceConfig(java.util.Set<java.lang.Float> anchors, optional float factorAtMin, optional float factorAtMax);
+ property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> AnimationSpec;
+ property public final float VelocityThreshold;
+ field public static final androidx.compose.material.SwipeableDefaults INSTANCE;
+ field public static final float StandardResistanceFactor = 10.0f;
+ field public static final float StiffResistanceFactor = 20.0f;
+ }
+
+ public final class SwipeableKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material.SwipeableState<T> rememberSwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.material.ExperimentalMaterialApi public static <T> androidx.compose.ui.Modifier swipeable(androidx.compose.ui.Modifier, androidx.compose.material.SwipeableState<T> state, java.util.Map<java.lang.Float,? extends T> anchors, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends androidx.compose.material.ThresholdConfig> thresholds, optional androidx.compose.material.ResistanceConfig? resistance, optional float velocityThreshold);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public class SwipeableState<T> {
+ ctor public SwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
+ method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? animateTo(T targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final T getCurrentValue();
+ method public final float getDirection();
+ method public final androidx.compose.runtime.State<java.lang.Float> getOffset();
+ method public final androidx.compose.runtime.State<java.lang.Float> getOverflow();
+ method public final androidx.compose.material.SwipeProgress<T> getProgress();
+ method public final T getTargetValue();
+ method public final boolean isAnimationRunning();
+ method public final float performDrag(float delta);
+ method public final suspend Object? performFling(float velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final T currentValue;
+ property @androidx.compose.material.ExperimentalMaterialApi public final float direction;
+ property public final boolean isAnimationRunning;
+ property public final androidx.compose.runtime.State<java.lang.Float> offset;
+ property public final androidx.compose.runtime.State<java.lang.Float> overflow;
+ property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.SwipeProgress<T> progress;
+ property @androidx.compose.material.ExperimentalMaterialApi public final T targetValue;
+ field public static final androidx.compose.material.SwipeableState.Companion Companion;
+ }
+
+ public static final class SwipeableState.Companion {
+ method public <T> androidx.compose.runtime.saveable.Saver<androidx.compose.material.SwipeableState<T>,T> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
+ }
+
+ @androidx.compose.runtime.Stable public interface SwitchColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
+ }
+
+ public final class SwitchDefaults {
+ method @androidx.compose.runtime.Composable public androidx.compose.material.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional float checkedTrackAlpha, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional float uncheckedTrackAlpha, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+ field public static final androidx.compose.material.SwitchDefaults INSTANCE;
+ }
+
+ public final class SwitchKt {
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SwitchColors colors);
+ }
+
+ public final class TabKt {
+ method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+ method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional long selectedContentColor, optional long unselectedContentColor);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TabPosition {
+ method public float getLeft();
+ method public float getRight();
+ method public float getWidth();
+ property public final float left;
+ property public final float right;
+ property public final float width;
+ }
+
+ public final class TabRowDefaults {
+ method @androidx.compose.runtime.Composable public void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+ method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+ method public float getDividerThickness();
+ method public float getIndicatorHeight();
+ method public float getScrollableTabRowPadding();
+ method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material.TabPosition currentTabPosition);
+ property public final float DividerThickness;
+ property public final float IndicatorHeight;
+ property public final float ScrollableTabRowPadding;
+ field public static final float DividerOpacity = 0.12f;
+ field public static final androidx.compose.material.TabRowDefaults INSTANCE;
+ }
+
+ public final class TabRowKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+ }
+
+ @androidx.compose.runtime.Stable public interface TextFieldColors {
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> cursorColor(boolean isError);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> indicatorColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> labelColor(boolean enabled, boolean error, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean isError);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> placeholderColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean enabled);
+ method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface TextFieldColorsWithIcons extends androidx.compose.material.TextFieldColors {
+ method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextFieldDefaults {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+ method public float getFocusedBorderThickness();
+ method public float getMinHeight();
+ method public float getMinWidth();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
+ method public float getUnfocusedBorderThickness();
+ method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.ui.Modifier indicatorLine(androidx.compose.ui.Modifier, boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional float focusedIndicatorLineThickness, optional float unfocusedIndicatorLineThickness);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+ method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
+ method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+ method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
+ method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
+ property public final float FocusedBorderThickness;
+ property public final float MinHeight;
+ property public final float MinWidth;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape OutlinedTextFieldShape;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape TextFieldShape;
+ property public final float UnfocusedBorderThickness;
+ field public static final float BackgroundOpacity = 0.12f;
+ field public static final androidx.compose.material.TextFieldDefaults INSTANCE;
+ field public static final float IconOpacity = 0.54f;
+ field public static final float UnfocusedIndicatorLineOpacity = 0.42f;
+ }
+
+ public final class TextFieldKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @Deprecated @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ method @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
+ }
+
+ public final class TextKt {
+ method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
+ method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Typography {
+ ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
+ method public androidx.compose.material.Typography copy(optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
+ method public androidx.compose.ui.text.TextStyle getBody1();
+ method public androidx.compose.ui.text.TextStyle getBody2();
+ method public androidx.compose.ui.text.TextStyle getButton();
+ method public androidx.compose.ui.text.TextStyle getCaption();
+ method public androidx.compose.ui.text.TextStyle getH1();
+ method public androidx.compose.ui.text.TextStyle getH2();
+ method public androidx.compose.ui.text.TextStyle getH3();
+ method public androidx.compose.ui.text.TextStyle getH4();
+ method public androidx.compose.ui.text.TextStyle getH5();
+ method public androidx.compose.ui.text.TextStyle getH6();
+ method public androidx.compose.ui.text.TextStyle getOverline();
+ method public androidx.compose.ui.text.TextStyle getSubtitle1();
+ method public androidx.compose.ui.text.TextStyle getSubtitle2();
+ property public final androidx.compose.ui.text.TextStyle body1;
+ property public final androidx.compose.ui.text.TextStyle body2;
+ property public final androidx.compose.ui.text.TextStyle button;
+ property public final androidx.compose.ui.text.TextStyle caption;
+ property public final androidx.compose.ui.text.TextStyle h1;
+ property public final androidx.compose.ui.text.TextStyle h2;
+ property public final androidx.compose.ui.text.TextStyle h3;
+ property public final androidx.compose.ui.text.TextStyle h4;
+ property public final androidx.compose.ui.text.TextStyle h5;
+ property public final androidx.compose.ui.text.TextStyle h6;
+ property public final androidx.compose.ui.text.TextStyle overline;
+ property public final androidx.compose.ui.text.TextStyle subtitle1;
+ property public final androidx.compose.ui.text.TextStyle subtitle2;
+ }
+
+}
+
+package androidx.compose.material.pullrefresh {
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshDefaults {
+ method public float getRefreshThreshold();
+ method public float getRefreshingOffset();
+ property public final float RefreshThreshold;
+ property public final float RefreshingOffset;
+ field public static final androidx.compose.material.pullrefresh.PullRefreshDefaults INSTANCE;
+ }
+
+ public final class PullRefreshIndicatorKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void PullRefreshIndicator(boolean refreshing, androidx.compose.material.pullrefresh.PullRefreshState state, optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional boolean scale);
+ }
+
+ public final class PullRefreshIndicatorTransformKt {
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefreshIndicatorTransform(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean scale);
+ }
+
+ public final class PullRefreshKt {
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean enabled);
+ method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> onPull, kotlin.jvm.functions.Function2<? super java.lang.Float,? super kotlin.coroutines.Continuation<? super java.lang.Float>,?> onRelease, optional boolean enabled);
+ }
+
+ @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshState {
+ method public float getProgress();
+ property public final float progress;
+ }
+
+ public final class PullRefreshStateKt {
+ method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.pullrefresh.PullRefreshState rememberPullRefreshState(boolean refreshing, kotlin.jvm.functions.Function0<kotlin.Unit> onRefresh, optional float refreshThreshold, optional float refreshingOffset);
+ }
+
+}
+
diff --git a/compose/material/material/api/restricted_1.5.0-beta01.txt b/compose/material/material/api/restricted_1.5.0-beta01.txt
index 3ce6f1ce..8935416 100644
--- a/compose/material/material/api/restricted_1.5.0-beta01.txt
+++ b/compose/material/material/api/restricted_1.5.0-beta01.txt
@@ -42,73 +42,11 @@
field public static final androidx.compose.material.BackdropScaffoldDefaults INSTANCE;
}
- public final class BackdropScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BackdropScaffold(kotlin.jvm.functions.Function0<kotlin.Unit> appBar, kotlin.jvm.functions.Function0<kotlin.Unit> backLayerContent, kotlin.jvm.functions.Function0<kotlin.Unit> frontLayerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BackdropScaffoldState scaffoldState, optional boolean gesturesEnabled, optional float peekHeight, optional float headerHeight, optional boolean persistentAppBar, optional boolean stickyFrontLayer, optional long backLayerBackgroundColor, optional long backLayerContentColor, optional androidx.compose.ui.graphics.Shape frontLayerShape, optional float frontLayerElevation, optional long frontLayerBackgroundColor, optional long frontLayerContentColor, optional long frontLayerScrimColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BackdropScaffoldState rememberBackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BackdropScaffoldState extends androidx.compose.material.SwipeableState<androidx.compose.material.BackdropValue> {
- ctor public BackdropScaffoldState(androidx.compose.material.BackdropValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- method public suspend Object? conceal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
- method public boolean isConcealed();
- method public boolean isRevealed();
- method public suspend Object? reveal(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final boolean isConcealed;
- property public final boolean isRevealed;
- property public final androidx.compose.material.SnackbarHostState snackbarHostState;
- field public static final androidx.compose.material.BackdropScaffoldState.Companion Companion;
- }
-
- public static final class BackdropScaffoldState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BackdropScaffoldState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BackdropValue,java.lang.Boolean> confirmStateChange, androidx.compose.material.SnackbarHostState snackbarHostState);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BackdropValue {
- method public static androidx.compose.material.BackdropValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BackdropValue[] values();
- enum_constant public static final androidx.compose.material.BackdropValue Concealed;
- enum_constant public static final androidx.compose.material.BackdropValue Revealed;
- }
-
public final class BadgeKt {
method @androidx.compose.runtime.Composable public static void Badge(optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? content);
method @androidx.compose.runtime.Composable public static void BadgedBox(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> badge, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
- @androidx.compose.material.ExperimentalMaterialApi public final class BottomDrawerState {
- ctor @Deprecated public BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.BottomDrawerValue getCurrentValue();
- method public float getOffset();
- method public androidx.compose.material.BottomDrawerValue getTargetValue();
- method public boolean isClosed();
- method public boolean isExpanded();
- method public boolean isOpen();
- method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.BottomDrawerValue currentValue;
- property public final boolean isClosed;
- property public final boolean isExpanded;
- property public final boolean isOpen;
- property public final float offset;
- property public final androidx.compose.material.BottomDrawerValue targetValue;
- field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
- }
-
- public static final class BottomDrawerState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(androidx.compose.ui.unit.Density density, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomDrawerState,androidx.compose.material.BottomDrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BottomDrawerValue {
- method public static androidx.compose.material.BottomDrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BottomDrawerValue[] values();
- enum_constant public static final androidx.compose.material.BottomDrawerValue Closed;
- enum_constant public static final androidx.compose.material.BottomDrawerValue Expanded;
- enum_constant public static final androidx.compose.material.BottomDrawerValue Open;
- }
-
public final class BottomNavigationDefaults {
method public float getElevation();
property public final float Elevation;
@@ -128,54 +66,6 @@
field public static final androidx.compose.material.BottomSheetScaffoldDefaults INSTANCE;
}
- public final class BottomSheetScaffoldKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomSheetScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.SnackbarHostState,kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional int floatingActionButtonPosition, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional float sheetPeekHeight, 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 drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long drawerScrimColor, optional long backgroundColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomSheetState BottomSheetScaffoldState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public static androidx.compose.material.BottomSheetState BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material.DrawerState drawerState, optional androidx.compose.material.BottomSheetState bottomSheetState, optional androidx.compose.material.SnackbarHostState snackbarHostState);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomSheetState rememberBottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetScaffoldState {
- ctor public BottomSheetScaffoldState(androidx.compose.material.DrawerState drawerState, androidx.compose.material.BottomSheetState bottomSheetState, androidx.compose.material.SnackbarHostState snackbarHostState);
- method public androidx.compose.material.BottomSheetState getBottomSheetState();
- method public androidx.compose.material.DrawerState getDrawerState();
- method public androidx.compose.material.SnackbarHostState getSnackbarHostState();
- property public final androidx.compose.material.BottomSheetState bottomSheetState;
- property public final androidx.compose.material.DrawerState drawerState;
- property public final androidx.compose.material.SnackbarHostState snackbarHostState;
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public final class BottomSheetState {
- ctor @Deprecated public BottomSheetState(androidx.compose.material.BottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmValueChange);
- method public suspend Object? collapse(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.BottomSheetValue getCurrentValue();
- method @Deprecated public float getOffset();
- method public float getProgress();
- method public boolean isCollapsed();
- method public boolean isExpanded();
- method public float requireOffset();
- property public final androidx.compose.material.BottomSheetValue currentValue;
- property public final boolean isCollapsed;
- property public final boolean isExpanded;
- property @Deprecated public final float offset;
- property public final float progress;
- field public static final androidx.compose.material.BottomSheetState.Companion Companion;
- }
-
- public static final class BottomSheetState.Companion {
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange);
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.BottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomSheetValue,java.lang.Boolean> confirmStateChange, androidx.compose.ui.unit.Density density);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum BottomSheetValue {
- method public static androidx.compose.material.BottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.BottomSheetValue[] values();
- enum_constant public static final androidx.compose.material.BottomSheetValue Collapsed;
- enum_constant public static final androidx.compose.material.BottomSheetValue Expanded;
- }
-
@androidx.compose.runtime.Stable public interface ButtonColors {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
@@ -219,8 +109,6 @@
public final class CardKt {
method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
@androidx.compose.runtime.Stable public interface CheckboxColors {
@@ -239,38 +127,6 @@
method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.CheckboxColors colors);
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ChipColors {
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconContentColor(boolean enabled);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ChipDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors chipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors filterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
- method public float getLeadingIconSize();
- method public float getMinHeight();
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedBorder();
- method public float getOutlinedBorderSize();
- method public float getSelectedIconSize();
- method @androidx.compose.runtime.Composable public androidx.compose.material.ChipColors outlinedChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconContentColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconContentColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.SelectableChipColors outlinedFilterChipColors(optional long backgroundColor, optional long contentColor, optional long leadingIconColor, optional long disabledBackgroundColor, optional long disabledContentColor, optional long disabledLeadingIconColor, optional long selectedBackgroundColor, optional long selectedContentColor, optional long selectedLeadingIconColor);
- property public final float LeadingIconSize;
- property public final float MinHeight;
- property public final float OutlinedBorderSize;
- property public final float SelectedIconSize;
- property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedBorder;
- field public static final float ContentOpacity = 0.87f;
- field public static final androidx.compose.material.ChipDefaults INSTANCE;
- field public static final float LeadingIconOpacity = 0.54f;
- field public static final float OutlinedBorderOpacity = 0.12f;
- }
-
- public final class ChipKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Chip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.ChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.material.SelectableChipColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? selectedIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- }
-
@androidx.compose.runtime.Stable public final class Colors {
ctor public Colors(long primary, long primaryVariant, long secondary, long secondaryVariant, long background, long surface, long error, long onPrimary, long onSecondary, long onBackground, long onSurface, long onError, boolean isLight);
method public androidx.compose.material.Colors copy(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onError, optional boolean isLight);
@@ -337,20 +193,6 @@
enum_constant public static final androidx.compose.material.DismissDirection StartToEnd;
}
- @androidx.compose.material.ExperimentalMaterialApi public final class DismissState extends androidx.compose.material.SwipeableState<androidx.compose.material.DismissValue> {
- ctor public DismissState(androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
- method public suspend Object? dismiss(androidx.compose.material.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.material.DismissDirection? getDismissDirection();
- method public boolean isDismissed(androidx.compose.material.DismissDirection direction);
- method public suspend Object? reset(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.DismissDirection? dismissDirection;
- field public static final androidx.compose.material.DismissState.Companion Companion;
- }
-
- public static final class DismissState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.DismissState,androidx.compose.material.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
- }
-
public enum DismissValue {
method public static androidx.compose.material.DismissValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
method public static androidx.compose.material.DismissValue[] values();
@@ -373,20 +215,14 @@
}
public final class DrawerKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void BottomDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.BottomDrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.BottomDrawerState BottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, androidx.compose.ui.unit.Density density, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
method @androidx.compose.runtime.Composable public static void ModalDrawer(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.DrawerState drawerState, optional boolean gesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerElevation, optional long drawerBackgroundColor, optional long drawerContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.BottomDrawerState rememberBottomDrawerState(androidx.compose.material.BottomDrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.BottomDrawerValue,java.lang.Boolean> confirmStateChange);
method @androidx.compose.runtime.Composable public static androidx.compose.material.DrawerState rememberDrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
}
@androidx.compose.runtime.Stable public final class DrawerState {
ctor public DrawerState(androidx.compose.material.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DrawerValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public suspend Object? animateTo(androidx.compose.material.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public androidx.compose.material.DrawerValue getCurrentValue();
- method @androidx.compose.material.ExperimentalMaterialApi public float getOffset();
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.material.DrawerValue getTargetValue();
method public boolean isAnimationRunning();
method public boolean isClosed();
method public boolean isOpen();
@@ -396,8 +232,6 @@
property public final boolean isAnimationRunning;
property public final boolean isClosed;
property public final boolean isOpen;
- property @androidx.compose.material.ExperimentalMaterialApi public final float offset;
- property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.DrawerValue targetValue;
field public static final androidx.compose.material.DrawerState.Companion Companion;
}
@@ -423,25 +257,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.ElevationOverlay> LocalElevationOverlay;
}
- @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterialApi {
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface ExposedDropdownMenuBoxScope {
- method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
- method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ExposedDropdownMenuDefaults {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TrailingIcon(boolean expanded, optional kotlin.jvm.functions.Function0<kotlin.Unit> onIconClick);
- method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long focusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- field public static final androidx.compose.material.ExposedDropdownMenuDefaults INSTANCE;
- }
-
- public final class ExposedDropdownMenuKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ExposedDropdownMenuBox(boolean expanded, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onExpandedChange, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material.ExposedDropdownMenuBoxScope,kotlin.Unit> content);
- }
-
@kotlin.jvm.JvmInline public final value class FabPosition {
field public static final androidx.compose.material.FabPosition.Companion Companion;
}
@@ -453,12 +268,6 @@
property public final int End;
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FixedThreshold implements androidx.compose.material.ThresholdConfig {
- ctor public FixedThreshold(float offset);
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.compose.material.FixedThreshold copy-0680j_4(float offset);
- }
-
public final class FloatingActionButtonDefaults {
method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation);
method @androidx.compose.runtime.Composable public androidx.compose.material.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float hoveredElevation, optional float focusedElevation);
@@ -474,12 +283,6 @@
method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional long backgroundColor, optional long contentColor, optional androidx.compose.material.FloatingActionButtonElevation elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class FractionalThreshold implements androidx.compose.material.ThresholdConfig {
- ctor public FractionalThreshold(float fraction);
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.compose.material.FractionalThreshold copy(float fraction);
- }
-
public final class IconButtonKt {
method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -492,15 +295,7 @@
}
public final class InteractiveComponentSizeKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumInteractiveComponentEnforcement();
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumTouchTargetEnforcement();
method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
- property @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumInteractiveComponentEnforcement;
- property @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumTouchTargetEnforcement;
- }
-
- public final class ListItemKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ListItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? secondaryText, optional boolean singleLineSecondaryText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineText, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailing, kotlin.jvm.functions.Function0<kotlin.Unit> text);
}
public final class MaterialTheme {
@@ -531,43 +326,6 @@
field public static final androidx.compose.material.ModalBottomSheetDefaults INSTANCE;
}
- public final class ModalBottomSheetKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void ModalBottomSheetLayout(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material.ModalBottomSheetState sheetState, optional boolean sheetGesturesEnabled, optional androidx.compose.ui.graphics.Shape sheetShape, optional float sheetElevation, optional long sheetBackgroundColor, optional long sheetContentColor, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.material.ModalBottomSheetState ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.ui.unit.Density density, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean isSkipHalfExpanded);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.ModalBottomSheetState rememberModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHalfExpanded);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class ModalBottomSheetState {
- ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional boolean isSkipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method public androidx.compose.material.ModalBottomSheetValue getCurrentValue();
- method public androidx.compose.material.ModalBottomSheetValue getTargetValue();
- method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public boolean isVisible();
- method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final androidx.compose.material.ModalBottomSheetValue currentValue;
- property public final boolean isVisible;
- property public final androidx.compose.material.ModalBottomSheetValue targetValue;
- field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
- }
-
- public static final class ModalBottomSheetState.Companion {
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, boolean skipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
- method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded);
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.material.ModalBottomSheetState,?> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmValueChange, boolean skipHalfExpanded, androidx.compose.ui.unit.Density density);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public enum ModalBottomSheetValue {
- method public static androidx.compose.material.ModalBottomSheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.material.ModalBottomSheetValue[] values();
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue Expanded;
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue HalfExpanded;
- enum_constant public static final androidx.compose.material.ModalBottomSheetValue Hidden;
- }
-
public final class NavigationRailDefaults {
method public float getElevation();
property public final float Elevation;
@@ -643,12 +401,6 @@
property public final androidx.compose.material.SnackbarHostState snackbarHostState;
}
- @androidx.compose.material.ExperimentalMaterialApi public interface SelectableChipColors {
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled, boolean selected);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean selected);
- method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean selected);
- }
-
@androidx.compose.runtime.Immutable public final class Shapes {
ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
method public androidx.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
@@ -677,7 +429,6 @@
}
public final class SliderKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
}
@@ -733,25 +484,6 @@
public final class SurfaceKt {
method @androidx.compose.runtime.Composable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional androidx.compose.foundation.BorderStroke? border, optional float elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class SwipeProgress<T> {
- ctor public SwipeProgress(T from, T to, float fraction);
- method public float getFraction();
- method public T getFrom();
- method public T getTo();
- property public final float fraction;
- property public final T from;
- property public final T to;
- }
-
- public final class SwipeToDismissKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material.DismissState state, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material.DismissDirection> directions, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissDirection,? extends androidx.compose.material.ThresholdConfig> dismissThresholds, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.DismissState rememberDismissState(optional androidx.compose.material.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material.DismissValue,java.lang.Boolean> confirmStateChange);
}
public final class SwipeableDefaults {
@@ -765,38 +497,6 @@
field public static final float StiffResistanceFactor = 20.0f;
}
- public final class SwipeableKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material.SwipeableState<T> rememberSwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi public static <T> androidx.compose.ui.Modifier swipeable(androidx.compose.ui.Modifier, androidx.compose.material.SwipeableState<T> state, java.util.Map<java.lang.Float,? extends T> anchors, androidx.compose.foundation.gestures.Orientation orientation, optional boolean enabled, optional boolean reverseDirection, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends androidx.compose.material.ThresholdConfig> thresholds, optional androidx.compose.material.ResistanceConfig? resistance, optional float velocityThreshold);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public class SwipeableState<T> {
- ctor public SwipeableState(T initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? animateTo(T targetValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public final T getCurrentValue();
- method public final float getDirection();
- method public final androidx.compose.runtime.State<java.lang.Float> getOffset();
- method public final androidx.compose.runtime.State<java.lang.Float> getOverflow();
- method public final androidx.compose.material.SwipeProgress<T> getProgress();
- method public final T getTargetValue();
- method public final boolean isAnimationRunning();
- method public final float performDrag(float delta);
- method public final suspend Object? performFling(float velocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.material.ExperimentalMaterialApi public final suspend Object? snapTo(T targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final T currentValue;
- property @androidx.compose.material.ExperimentalMaterialApi public final float direction;
- property public final boolean isAnimationRunning;
- property public final androidx.compose.runtime.State<java.lang.Float> offset;
- property public final androidx.compose.runtime.State<java.lang.Float> overflow;
- property @androidx.compose.material.ExperimentalMaterialApi public final androidx.compose.material.SwipeProgress<T> progress;
- property @androidx.compose.material.ExperimentalMaterialApi public final T targetValue;
- field public static final androidx.compose.material.SwipeableState.Companion Companion;
- }
-
- public static final class SwipeableState.Companion {
- method public <T> androidx.compose.runtime.saveable.Saver<androidx.compose.material.SwipeableState<T>,T> Saver(androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> confirmStateChange);
- }
-
@androidx.compose.runtime.Stable public interface SwitchColors {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
@@ -856,27 +556,15 @@
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError);
}
- @androidx.compose.material.ExperimentalMaterialApi @kotlin.jvm.JvmDefaultWithCompatibility public interface TextFieldColorsWithIcons extends androidx.compose.material.TextFieldColors {
- method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
- method @androidx.compose.runtime.Composable public default androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconColor(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource);
- }
-
@androidx.compose.runtime.Immutable public final class TextFieldDefaults {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getOutlinedTextFieldShape();
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getTextFieldShape();
method public float getUnfocusedBorderThickness();
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.ui.Modifier indicatorLine(androidx.compose.ui.Modifier, boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional float focusedIndicatorLineThickness, optional float unfocusedIndicatorLineThickness);
method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
method @androidx.compose.runtime.Composable public androidx.compose.material.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long backgroundColor, optional long cursorColor, optional long errorCursorColor, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long leadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long trailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long placeholderColor, optional long disabledPlaceholderColor);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
- method @androidx.compose.material.ExperimentalMaterialApi public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
property public final float FocusedBorderThickness;
property public final float MinHeight;
property public final float MinWidth;
@@ -906,10 +594,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
- @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
- method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- }
-
@androidx.compose.runtime.Immutable public final class Typography {
ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
method public androidx.compose.material.Typography copy(optional androidx.compose.ui.text.TextStyle h1, optional androidx.compose.ui.text.TextStyle h2, optional androidx.compose.ui.text.TextStyle h3, optional androidx.compose.ui.text.TextStyle h4, optional androidx.compose.ui.text.TextStyle h5, optional androidx.compose.ui.text.TextStyle h6, optional androidx.compose.ui.text.TextStyle subtitle1, optional androidx.compose.ui.text.TextStyle subtitle2, optional androidx.compose.ui.text.TextStyle body1, optional androidx.compose.ui.text.TextStyle body2, optional androidx.compose.ui.text.TextStyle button, optional androidx.compose.ui.text.TextStyle caption, optional androidx.compose.ui.text.TextStyle overline);
@@ -943,37 +627,3 @@
}
-package androidx.compose.material.pullrefresh {
-
- @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshDefaults {
- method public float getRefreshThreshold();
- method public float getRefreshingOffset();
- property public final float RefreshThreshold;
- property public final float RefreshingOffset;
- field public static final androidx.compose.material.pullrefresh.PullRefreshDefaults INSTANCE;
- }
-
- public final class PullRefreshIndicatorKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void PullRefreshIndicator(boolean refreshing, androidx.compose.material.pullrefresh.PullRefreshState state, optional androidx.compose.ui.Modifier modifier, optional long backgroundColor, optional long contentColor, optional boolean scale);
- }
-
- public final class PullRefreshIndicatorTransformKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefreshIndicatorTransform(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean scale);
- }
-
- public final class PullRefreshKt {
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, androidx.compose.material.pullrefresh.PullRefreshState state, optional boolean enabled);
- method @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.ui.Modifier pullRefresh(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> onPull, kotlin.jvm.functions.Function2<? super java.lang.Float,? super kotlin.coroutines.Continuation<? super java.lang.Float>,?> onRelease, optional boolean enabled);
- }
-
- @androidx.compose.material.ExperimentalMaterialApi public final class PullRefreshState {
- method public float getProgress();
- property public final float progress;
- }
-
- public final class PullRefreshStateKt {
- method @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static androidx.compose.material.pullrefresh.PullRefreshState rememberPullRefreshState(boolean refreshing, kotlin.jvm.functions.Function0<kotlin.Unit> onRefresh, optional float refreshThreshold, optional float refreshingOffset);
- }
-
-}
-
diff --git a/compose/runtime/runtime-livedata/api/public_plus_experimental_1.5.0-beta01.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..192cf6a
--- /dev/null
+++ b/compose/runtime/runtime-livedata/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.compose.runtime.livedata {
+
+ public final class LiveDataAdapterKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> observeAsState(androidx.lifecycle.LiveData<T>);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> observeAsState(androidx.lifecycle.LiveData<T>, R initial);
+ }
+
+}
+
diff --git a/compose/runtime/runtime-rxjava2/api/public_plus_experimental_1.5.0-beta01.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..43f9fd4
--- /dev/null
+++ b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.compose.runtime.rxjava2 {
+
+ public final class RxJava2AdapterKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> subscribeAsState(io.reactivex.Completable);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.Flowable<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.Maybe<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.Observable<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.Single<T>, R initial);
+ }
+
+}
+
diff --git a/compose/runtime/runtime-rxjava3/api/public_plus_experimental_1.5.0-beta01.txt b/compose/runtime/runtime-rxjava3/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..357541c8
--- /dev/null
+++ b/compose/runtime/runtime-rxjava3/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.compose.runtime.rxjava3 {
+
+ public final class RxJava3AdapterKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Boolean> subscribeAsState(io.reactivex.rxjava3.core.Completable);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.rxjava3.core.Flowable<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.rxjava3.core.Maybe<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.rxjava3.core.Observable<T>, R initial);
+ method @androidx.compose.runtime.Composable public static <R, T extends R> androidx.compose.runtime.State<R> subscribeAsState(io.reactivex.rxjava3.core.Single<T>, R initial);
+ }
+
+}
+
diff --git a/compose/runtime/runtime-saveable/api/public_plus_experimental_1.5.0-beta01.txt b/compose/runtime/runtime-saveable/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..6cf4941
--- /dev/null
+++ b/compose/runtime/runtime-saveable/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,58 @@
+// Signature format: 4.0
+package androidx.compose.runtime.saveable {
+
+ public final class ListSaverKt {
+ method public static <Original, Saveable> androidx.compose.runtime.saveable.Saver<Original,java.lang.Object> listSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.saveable.SaverScope,? super Original,? extends java.util.List<? extends Saveable>> save, kotlin.jvm.functions.Function1<? super java.util.List<? extends Saveable>,? extends Original> restore);
+ }
+
+ public final class MapSaverKt {
+ method public static <T> androidx.compose.runtime.saveable.Saver<T,java.lang.Object> mapSaver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.saveable.SaverScope,? super T,? extends java.util.Map<java.lang.String,?>> save, kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,?>,? extends T> restore);
+ }
+
+ public final class RememberSaveableKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.MutableState<T> rememberSaveable(Object![]? inputs, androidx.compose.runtime.saveable.Saver<T,?> stateSaver, optional String? key, kotlin.jvm.functions.Function0<? extends androidx.compose.runtime.MutableState<T>> init);
+ method @androidx.compose.runtime.Composable public static <T> T rememberSaveable(Object![]? inputs, optional androidx.compose.runtime.saveable.Saver<T,?> saver, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+ }
+
+ public interface SaveableStateHolder {
+ method @androidx.compose.runtime.Composable public void SaveableStateProvider(Object key, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public void removeState(Object key);
+ }
+
+ public final class SaveableStateHolderKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.saveable.SaveableStateHolder rememberSaveableStateHolder();
+ }
+
+ public interface SaveableStateRegistry {
+ method public boolean canBeSaved(Object value);
+ method public Object? consumeRestored(String key);
+ method public java.util.Map<java.lang.String,java.util.List<java.lang.Object>> performSave();
+ method public androidx.compose.runtime.saveable.SaveableStateRegistry.Entry registerProvider(String key, kotlin.jvm.functions.Function0<?> valueProvider);
+ }
+
+ public static interface SaveableStateRegistry.Entry {
+ method public void unregister();
+ }
+
+ public final class SaveableStateRegistryKt {
+ method public static androidx.compose.runtime.saveable.SaveableStateRegistry SaveableStateRegistry(java.util.Map<java.lang.String,? extends java.util.List<?>>? restoredValues, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> canBeSaved);
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.runtime.saveable.SaveableStateRegistry> getLocalSaveableStateRegistry();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.runtime.saveable.SaveableStateRegistry> LocalSaveableStateRegistry;
+ }
+
+ public interface Saver<Original, Saveable> {
+ method public Original? restore(Saveable value);
+ method public Saveable? save(androidx.compose.runtime.saveable.SaverScope, Original value);
+ }
+
+ public final class SaverKt {
+ method public static <Original, Saveable> androidx.compose.runtime.saveable.Saver<Original,Saveable> Saver(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.saveable.SaverScope,? super Original,? extends Saveable> save, kotlin.jvm.functions.Function1<? super Saveable,? extends Original> restore);
+ method public static <T> androidx.compose.runtime.saveable.Saver<T,java.lang.Object> autoSaver();
+ }
+
+ public fun interface SaverScope {
+ method public boolean canBeSaved(Object value);
+ }
+
+}
+
diff --git a/compose/runtime/runtime/api/1.5.0-beta01.txt b/compose/runtime/runtime/api/1.5.0-beta01.txt
index 095c02f..ac9ddb4 100644
--- a/compose/runtime/runtime/api/1.5.0-beta01.txt
+++ b/compose/runtime/runtime/api/1.5.0-beta01.txt
@@ -47,11 +47,6 @@
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface Composable {
}
- @androidx.compose.runtime.InternalComposeApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ComposableInferredTarget {
- method public abstract String scheme();
- property public abstract String scheme;
- }
-
@kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface ComposableOpenTarget {
method public abstract int index();
property public abstract int index;
@@ -104,7 +99,6 @@
public sealed interface Composer {
method @androidx.compose.runtime.ComposeCompilerApi public <V, T> void apply(V value, kotlin.jvm.functions.Function2<? super T,? super V,kotlin.Unit> block);
- method @androidx.compose.runtime.InternalComposeApi public androidx.compose.runtime.CompositionContext buildContext();
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(boolean value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(byte value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(char value);
@@ -116,7 +110,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(short value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
method public void collectParameterInformation();
- method @androidx.compose.runtime.InternalComposeApi public <T> T consume(androidx.compose.runtime.CompositionLocal<T> key);
method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
@@ -125,7 +118,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void endNode();
- method @androidx.compose.runtime.InternalComposeApi public void endProviders();
method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void endReusableGroup();
@@ -142,11 +134,7 @@
method public androidx.compose.runtime.RecomposeScope? getRecomposeScope();
method public Object? getRecomposeScopeIdentity();
method public boolean getSkipping();
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(androidx.compose.runtime.MovableContent<?> value, Object? parameter);
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContentReferences(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
- method @androidx.compose.runtime.InternalComposeApi public void recordSideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
- method @androidx.compose.runtime.InternalComposeApi public void recordUsed(androidx.compose.runtime.RecomposeScope scope);
method @androidx.compose.runtime.ComposeCompilerApi public Object? rememberedValue();
method @androidx.compose.runtime.ComposeCompilerApi public void skipCurrentGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void skipToGroupEnd();
@@ -156,7 +144,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void startDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void startMovableGroup(int key, Object? dataKey);
method @androidx.compose.runtime.ComposeCompilerApi public void startNode();
- method @androidx.compose.runtime.InternalComposeApi public void startProviders(androidx.compose.runtime.ProvidedValue<?>![] values);
method @androidx.compose.runtime.ComposeCompilerApi public void startReplaceableGroup(int key);
method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.Composer startRestartGroup(int key);
method @androidx.compose.runtime.ComposeCompilerApi public void startReusableGroup(int key, Object? dataKey);
@@ -164,7 +151,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void updateRememberedValue(Object? value);
method @androidx.compose.runtime.ComposeCompilerApi public void useNode();
property public abstract androidx.compose.runtime.Applier<?> applier;
- property @androidx.compose.runtime.InternalComposeApi @org.jetbrains.annotations.TestOnly public abstract kotlin.coroutines.CoroutineContext applyCoroutineContext;
property @org.jetbrains.annotations.TestOnly public abstract androidx.compose.runtime.ControlledComposition composition;
property public abstract androidx.compose.runtime.tooling.CompositionData compositionData;
property public abstract int compoundKeyHash;
@@ -180,7 +166,6 @@
public static final class Composer.Companion {
method public Object getEmpty();
- method @androidx.compose.runtime.InternalComposeTracingApi public void setTracer(androidx.compose.runtime.CompositionTracer tracer);
property public final Object Empty;
}
@@ -211,10 +196,7 @@
public final class CompositionKt {
method public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
method @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
- method @androidx.compose.runtime.ExperimentalComposeApi @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
- method @androidx.compose.runtime.ExperimentalComposeApi public static kotlin.coroutines.CoroutineContext getRecomposeCoroutineContext(androidx.compose.runtime.ControlledComposition);
}
@androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
@@ -242,21 +224,13 @@
property public final androidx.compose.runtime.CompositionLocalMap Empty;
}
- @androidx.compose.runtime.InternalComposeTracingApi public interface CompositionTracer {
- method public boolean isTraceInProgress();
- method public void traceEventEnd();
- method public void traceEventStart(int key, int dirty1, int dirty2, String info);
- }
-
public sealed interface ControlledComposition extends androidx.compose.runtime.Composition {
method public void applyChanges();
method public void applyLateChanges();
method public void changesApplied();
method public void composeContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
method public <R> R delegateInvalidations(androidx.compose.runtime.ControlledComposition? to, int groupIndex, kotlin.jvm.functions.Function0<? extends R> block);
- method @androidx.compose.runtime.InternalComposeApi public void disposeUnusedMovableContent(androidx.compose.runtime.MovableContentState state);
method public boolean getHasPendingChanges();
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
method public void invalidateAll();
method public boolean isComposing();
method public boolean observesAnyOf(java.util.Set<?> values);
@@ -265,7 +239,6 @@
method public void recordModificationsOf(java.util.Set<?> values);
method public void recordReadOf(Object value);
method public void recordWriteOf(Object value);
- method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
property public abstract boolean hasPendingChanges;
property public abstract boolean isComposing;
}
@@ -304,9 +277,6 @@
method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
}
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is an experimental API for Compose and is likely to change before becoming " + "stable.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalComposeApi {
- }
-
@kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExplicitGroupsComposable {
}
@@ -327,12 +297,6 @@
property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
}
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API for Compose modules that may change frequently " + "and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalComposeApi {
- }
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeTracingApi {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LongState extends androidx.compose.runtime.State<java.lang.Long> {
method public long getLongValue();
method public default Long getValue();
@@ -357,12 +321,6 @@
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
}
- @androidx.compose.runtime.InternalComposeApi public final class MovableContent<P> {
- ctor public MovableContent(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
- method public kotlin.jvm.functions.Function1<P,kotlin.Unit> getContent();
- property public final kotlin.jvm.functions.Function1<P,kotlin.Unit> content;
- }
-
public final class MovableContentKt {
method public static kotlin.jvm.functions.Function0<kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function0<kotlin.Unit> content);
method public static <P> kotlin.jvm.functions.Function1<P,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
@@ -375,12 +333,6 @@
method public static <R, P1, P2, P3> kotlin.jvm.functions.Function4<R,P1,P2,P3,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function4<? super R,? super P1,? super P2,? super P3,kotlin.Unit> content);
}
- @androidx.compose.runtime.InternalComposeApi public final class MovableContentState {
- }
-
- @androidx.compose.runtime.InternalComposeApi public final class MovableContentStateReference {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableDoubleState extends androidx.compose.runtime.DoubleState androidx.compose.runtime.MutableState<java.lang.Double> {
method public void setDoubleValue(double);
method public default void setValue(double);
@@ -477,7 +429,6 @@
method public void pauseCompositionFrameClock();
method public void resumeCompositionFrameClock();
method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.runtime.ExperimentalComposeApi public suspend Object? runRecomposeConcurrentlyAndApplyChanges(kotlin.coroutines.CoroutineContext recomposeCoroutineContext, kotlin.coroutines.Continuation<? super kotlin.Unit>);
property public final long changeCount;
property public final kotlinx.coroutines.flow.StateFlow<androidx.compose.runtime.Recomposer.State> currentState;
property public kotlin.coroutines.CoroutineContext effectCoroutineContext;
@@ -705,20 +656,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambdaN composableLambdaNInstance(int key, boolean tracked, int arity, Object block);
}
- @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface Decoy {
- method public abstract String[] signature();
- method public abstract String targetName();
- property public abstract String[] signature;
- property public abstract String targetName;
- }
-
- @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface DecoyImplementation {
- method public abstract long id();
- method public abstract String name();
- property public abstract long id;
- property public abstract String name;
- }
-
public final class DecoyKt {
method @androidx.compose.runtime.ComposeCompilerApi public static Void illegalDecoyCallException(String fName);
}
@@ -754,10 +691,7 @@
}
public final class LiveLiteralKt {
- method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
method public static boolean isLiveLiteralsEnabled();
- method @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T value);
- method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
property public static final boolean isLiveLiteralsEnabled;
}
@@ -822,8 +756,6 @@
method public abstract androidx.compose.runtime.snapshots.Snapshot getRoot();
method public abstract boolean hasPendingChanges();
method public abstract androidx.compose.runtime.snapshots.Snapshot takeNestedSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver);
- method @androidx.compose.runtime.ExperimentalComposeApi public final androidx.compose.runtime.snapshots.Snapshot? unsafeEnter();
- method @androidx.compose.runtime.ExperimentalComposeApi public final void unsafeLeave(androidx.compose.runtime.snapshots.Snapshot? oldSnapshot);
property public int id;
property public abstract boolean readOnly;
property public abstract androidx.compose.runtime.snapshots.Snapshot root;
@@ -836,7 +768,6 @@
method public boolean isApplyObserverNotificationPending();
method public void notifyObjectsInitialized();
method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
- method @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
method public androidx.compose.runtime.snapshots.ObserverHandle registerApplyObserver(kotlin.jvm.functions.Function2<? super java.util.Set<?>,? super androidx.compose.runtime.snapshots.Snapshot,kotlin.Unit> observer);
method public androidx.compose.runtime.snapshots.ObserverHandle registerGlobalWriteObserver(kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit> observer);
method public void sendApplyNotifications();
@@ -876,17 +807,6 @@
field public static final androidx.compose.runtime.snapshots.SnapshotApplyResult.Success INSTANCE;
}
- @androidx.compose.runtime.ExperimentalComposeApi public interface SnapshotContextElement extends kotlin.coroutines.CoroutineContext.Element {
- field public static final androidx.compose.runtime.snapshots.SnapshotContextElement.Key Key;
- }
-
- public static final class SnapshotContextElement.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.snapshots.SnapshotContextElement> {
- }
-
- public final class SnapshotContextElementKt {
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.snapshots.SnapshotContextElement asContextElement(androidx.compose.runtime.snapshots.Snapshot);
- }
-
public final class SnapshotKt {
method public static <T extends androidx.compose.runtime.snapshots.StateRecord> T readable(T, androidx.compose.runtime.snapshots.StateObject state);
method public static <T extends androidx.compose.runtime.snapshots.StateRecord> T readable(T, androidx.compose.runtime.snapshots.StateObject state, androidx.compose.runtime.snapshots.Snapshot snapshot);
diff --git a/compose/runtime/runtime/api/public_plus_experimental_1.5.0-beta01.txt b/compose/runtime/runtime/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..095c02f
--- /dev/null
+++ b/compose/runtime/runtime/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,1021 @@
+// Signature format: 4.0
+package androidx.compose.runtime {
+
+ public abstract class AbstractApplier<T> implements androidx.compose.runtime.Applier<T> {
+ ctor public AbstractApplier(T root);
+ method public final void clear();
+ method public void down(T node);
+ method public T getCurrent();
+ method public final T getRoot();
+ method protected final void move(java.util.List<T>, int from, int to, int count);
+ method protected abstract void onClear();
+ method protected final void remove(java.util.List<T>, int index, int count);
+ method protected void setCurrent(T!);
+ method public void up();
+ property public T current;
+ property public final T root;
+ }
+
+ public final class ActualAndroid_androidKt {
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ property @Deprecated public static final androidx.compose.runtime.MonotonicFrameClock DefaultMonotonicFrameClock;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface Applier<N> {
+ method public void clear();
+ method public void down(N node);
+ method public N getCurrent();
+ method public void insertBottomUp(int index, N instance);
+ method public void insertTopDown(int index, N instance);
+ method public void move(int from, int to, int count);
+ method public default void onBeginChanges();
+ method public default void onEndChanges();
+ method public void remove(int index, int count);
+ method public void up();
+ property public abstract N current;
+ }
+
+ public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+ ctor public BroadcastFrameClock(optional kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
+ method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
+ method public boolean getHasAwaiters();
+ method public void sendFrame(long timeNanos);
+ method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ property public final boolean hasAwaiters;
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface Composable {
+ }
+
+ @androidx.compose.runtime.InternalComposeApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ComposableInferredTarget {
+ method public abstract String scheme();
+ property public abstract String scheme;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface ComposableOpenTarget {
+ method public abstract int index();
+ property public abstract int index;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface ComposableTarget {
+ method public abstract String applier();
+ property public abstract String applier;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS) public @interface ComposableTargetMarker {
+ method public abstract String description() default "";
+ property public abstract String description;
+ }
+
+ public final class ComposablesKt {
+ method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update);
+ method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ReusableComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update);
+ method @androidx.compose.runtime.Composable public static inline <T extends java.lang.Object, reified E extends androidx.compose.runtime.Applier<?>> void ReusableComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable public static inline <T, reified E extends androidx.compose.runtime.Applier<?>> void ReusableComposeNode(kotlin.jvm.functions.Function0<? extends T> factory, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> update, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.SkippableUpdater<T>,kotlin.Unit> skippableUpdate, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static inline void ReusableContent(Object? key, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable public static inline void ReusableContentHost(boolean active, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.runtime.Composer getCurrentComposer();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable public static int getCurrentCompositeKeyHash();
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.CompositionLocalContext getCurrentCompositionLocalContext();
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.runtime.RecomposeScope getCurrentRecomposeScope();
+ method @androidx.compose.runtime.Composable public static inline <T> T key(Object![]? keys, kotlin.jvm.functions.Function0<? extends T> block);
+ method @androidx.compose.runtime.Composable public static inline <T> T remember(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.Composable public static inline <T> T remember(Object? key1, Object? key2, kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.Composable public static inline <T> T remember(Object? key1, kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.Composable public static inline <T> T remember(Object![]? keys, kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.Composable public static inline <T> T remember(kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.CompositionContext rememberCompositionContext();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static final androidx.compose.runtime.Composer currentComposer;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable public static final int currentCompositeKeyHash;
+ property @androidx.compose.runtime.Composable public static final androidx.compose.runtime.CompositionLocalContext currentCompositionLocalContext;
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static final androidx.compose.runtime.RecomposeScope currentRecomposeScope;
+ }
+
+ @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.TYPEALIAS}) public @interface ComposeCompilerApi {
+ }
+
+ public interface ComposeNodeLifecycleCallback {
+ method public void onDeactivate();
+ method public void onRelease();
+ method public void onReuse();
+ }
+
+ public sealed interface Composer {
+ method @androidx.compose.runtime.ComposeCompilerApi public <V, T> void apply(V value, kotlin.jvm.functions.Function2<? super T,? super V,kotlin.Unit> block);
+ method @androidx.compose.runtime.InternalComposeApi public androidx.compose.runtime.CompositionContext buildContext();
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(boolean value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(byte value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(char value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(double value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(float value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(int value);
+ method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(long value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(short value);
+ method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
+ method public void collectParameterInformation();
+ method @androidx.compose.runtime.InternalComposeApi public <T> T consume(androidx.compose.runtime.CompositionLocal<T> key);
+ method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
+ method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
+ method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
+ method @org.jetbrains.annotations.TestOnly public void disableSourceInformation();
+ method @androidx.compose.runtime.ComposeCompilerApi public void enableReusing();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endNode();
+ method @androidx.compose.runtime.InternalComposeApi public void endProviders();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
+ method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endReusableGroup();
+ method @androidx.compose.runtime.ComposeCompilerApi public void endToMarker(int marker);
+ method public androidx.compose.runtime.Applier<?> getApplier();
+ method @org.jetbrains.annotations.TestOnly public kotlin.coroutines.CoroutineContext getApplyCoroutineContext();
+ method @org.jetbrains.annotations.TestOnly public androidx.compose.runtime.ControlledComposition getComposition();
+ method public androidx.compose.runtime.tooling.CompositionData getCompositionData();
+ method public int getCompoundKeyHash();
+ method public androidx.compose.runtime.CompositionLocalMap getCurrentCompositionLocalMap();
+ method public int getCurrentMarker();
+ method public boolean getDefaultsInvalid();
+ method public boolean getInserting();
+ method public androidx.compose.runtime.RecomposeScope? getRecomposeScope();
+ method public Object? getRecomposeScopeIdentity();
+ method public boolean getSkipping();
+ method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(androidx.compose.runtime.MovableContent<?> value, Object? parameter);
+ method @androidx.compose.runtime.InternalComposeApi public void insertMovableContentReferences(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
+ method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
+ method @androidx.compose.runtime.InternalComposeApi public void recordSideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+ method @androidx.compose.runtime.InternalComposeApi public void recordUsed(androidx.compose.runtime.RecomposeScope scope);
+ method @androidx.compose.runtime.ComposeCompilerApi public Object? rememberedValue();
+ method @androidx.compose.runtime.ComposeCompilerApi public void skipCurrentGroup();
+ method @androidx.compose.runtime.ComposeCompilerApi public void skipToGroupEnd();
+ method public void sourceInformation(String sourceInformation);
+ method public void sourceInformationMarkerEnd();
+ method public void sourceInformationMarkerStart(int key, String sourceInformation);
+ method @androidx.compose.runtime.ComposeCompilerApi public void startDefaults();
+ method @androidx.compose.runtime.ComposeCompilerApi public void startMovableGroup(int key, Object? dataKey);
+ method @androidx.compose.runtime.ComposeCompilerApi public void startNode();
+ method @androidx.compose.runtime.InternalComposeApi public void startProviders(androidx.compose.runtime.ProvidedValue<?>![] values);
+ method @androidx.compose.runtime.ComposeCompilerApi public void startReplaceableGroup(int key);
+ method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.Composer startRestartGroup(int key);
+ method @androidx.compose.runtime.ComposeCompilerApi public void startReusableGroup(int key, Object? dataKey);
+ method @androidx.compose.runtime.ComposeCompilerApi public void startReusableNode();
+ method @androidx.compose.runtime.ComposeCompilerApi public void updateRememberedValue(Object? value);
+ method @androidx.compose.runtime.ComposeCompilerApi public void useNode();
+ property public abstract androidx.compose.runtime.Applier<?> applier;
+ property @androidx.compose.runtime.InternalComposeApi @org.jetbrains.annotations.TestOnly public abstract kotlin.coroutines.CoroutineContext applyCoroutineContext;
+ property @org.jetbrains.annotations.TestOnly public abstract androidx.compose.runtime.ControlledComposition composition;
+ property public abstract androidx.compose.runtime.tooling.CompositionData compositionData;
+ property public abstract int compoundKeyHash;
+ property public abstract androidx.compose.runtime.CompositionLocalMap currentCompositionLocalMap;
+ property public abstract int currentMarker;
+ property public abstract boolean defaultsInvalid;
+ property public abstract boolean inserting;
+ property public abstract androidx.compose.runtime.RecomposeScope? recomposeScope;
+ property public abstract Object? recomposeScopeIdentity;
+ property public abstract boolean skipping;
+ field public static final androidx.compose.runtime.Composer.Companion Companion;
+ }
+
+ public static final class Composer.Companion {
+ method public Object getEmpty();
+ method @androidx.compose.runtime.InternalComposeTracingApi public void setTracer(androidx.compose.runtime.CompositionTracer tracer);
+ property public final Object Empty;
+ }
+
+ public final class ComposerKt {
+ method @androidx.compose.runtime.ComposeCompilerApi public static inline <T> T cache(androidx.compose.runtime.Composer, boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static boolean isTraceInProgress();
+ method @androidx.compose.runtime.ComposeCompilerApi public static void sourceInformation(androidx.compose.runtime.Composer composer, String sourceInformation);
+ method @androidx.compose.runtime.ComposeCompilerApi public static void sourceInformationMarkerEnd(androidx.compose.runtime.Composer composer);
+ method @androidx.compose.runtime.ComposeCompilerApi public static void sourceInformationMarkerStart(androidx.compose.runtime.Composer composer, int key, String sourceInformation);
+ method @androidx.compose.runtime.ComposeCompilerApi public static void traceEventEnd();
+ method @androidx.compose.runtime.ComposeCompilerApi public static void traceEventStart(int key, int dirty1, int dirty2, String info);
+ method @Deprecated @androidx.compose.runtime.ComposeCompilerApi public static void traceEventStart(int key, String info);
+ }
+
+ public interface Composition {
+ method public void dispose();
+ method public boolean getHasInvalidations();
+ method public boolean isDisposed();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ property public abstract boolean hasInvalidations;
+ property public abstract boolean isDisposed;
+ }
+
+ public abstract class CompositionContext {
+ method public abstract kotlin.coroutines.CoroutineContext getEffectCoroutineContext();
+ property public abstract kotlin.coroutines.CoroutineContext effectCoroutineContext;
+ }
+
+ public final class CompositionKt {
+ method public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
+ method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
+ method @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
+ method @androidx.compose.runtime.ExperimentalComposeApi @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
+ method @androidx.compose.runtime.ExperimentalComposeApi public static kotlin.coroutines.CoroutineContext getRecomposeCoroutineContext(androidx.compose.runtime.ControlledComposition);
+ }
+
+ @androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T getCurrent();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final inline T current;
+ }
+
+ @androidx.compose.runtime.Stable public final class CompositionLocalContext {
+ }
+
+ public final class CompositionLocalKt {
+ method @androidx.compose.runtime.Composable public static void CompositionLocalProvider(androidx.compose.runtime.CompositionLocalContext context, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void CompositionLocalProvider(androidx.compose.runtime.ProvidedValue<?>![] values, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static <T> androidx.compose.runtime.ProvidableCompositionLocal<T> compositionLocalOf(optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> defaultFactory);
+ method public static <T> androidx.compose.runtime.ProvidableCompositionLocal<T> staticCompositionLocalOf(kotlin.jvm.functions.Function0<? extends T> defaultFactory);
+ }
+
+ public sealed interface CompositionLocalMap {
+ method public operator <T> T get(androidx.compose.runtime.CompositionLocal<T> key);
+ field public static final androidx.compose.runtime.CompositionLocalMap.Companion Companion;
+ }
+
+ public static final class CompositionLocalMap.Companion {
+ method public androidx.compose.runtime.CompositionLocalMap getEmpty();
+ property public final androidx.compose.runtime.CompositionLocalMap Empty;
+ }
+
+ @androidx.compose.runtime.InternalComposeTracingApi public interface CompositionTracer {
+ method public boolean isTraceInProgress();
+ method public void traceEventEnd();
+ method public void traceEventStart(int key, int dirty1, int dirty2, String info);
+ }
+
+ public sealed interface ControlledComposition extends androidx.compose.runtime.Composition {
+ method public void applyChanges();
+ method public void applyLateChanges();
+ method public void changesApplied();
+ method public void composeContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public <R> R delegateInvalidations(androidx.compose.runtime.ControlledComposition? to, int groupIndex, kotlin.jvm.functions.Function0<? extends R> block);
+ method @androidx.compose.runtime.InternalComposeApi public void disposeUnusedMovableContent(androidx.compose.runtime.MovableContentState state);
+ method public boolean getHasPendingChanges();
+ method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
+ method public void invalidateAll();
+ method public boolean isComposing();
+ method public boolean observesAnyOf(java.util.Set<?> values);
+ method public void prepareCompose(kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public boolean recompose();
+ method public void recordModificationsOf(java.util.Set<?> values);
+ method public void recordReadOf(Object value);
+ method public void recordWriteOf(Object value);
+ method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
+ property public abstract boolean hasPendingChanges;
+ property public abstract boolean isComposing;
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.TYPE) public @interface DisallowComposableCalls {
+ }
+
+ public interface DisposableEffectResult {
+ method public void dispose();
+ }
+
+ public final class DisposableEffectScope {
+ ctor public DisposableEffectScope();
+ method public inline androidx.compose.runtime.DisposableEffectResult onDispose(kotlin.jvm.functions.Function0<kotlin.Unit> onDisposeEffect);
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface DoubleState extends androidx.compose.runtime.State<java.lang.Double> {
+ method public double getDoubleValue();
+ method public default Double getValue();
+ property public abstract double doubleValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
+ }
+
+ public final class EffectsKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object? key1, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(Object![]? keys, kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void DisposableEffect(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.DisposableEffectScope,? extends androidx.compose.runtime.DisposableEffectResult> effect);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+ method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is an experimental API for Compose and is likely to change before becoming " + "stable.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalComposeApi {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExplicitGroupsComposable {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface FloatState extends androidx.compose.runtime.State<java.lang.Float> {
+ method public float getFloatValue();
+ method public default Float getValue();
+ property public abstract float floatValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
+ }
+
+ @androidx.compose.runtime.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface Immutable {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface IntState extends androidx.compose.runtime.State<java.lang.Integer> {
+ method public int getIntValue();
+ method public default Integer getValue();
+ property public abstract int intValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API for Compose modules that may change frequently " + "and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalComposeApi {
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeTracingApi {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LongState extends androidx.compose.runtime.State<java.lang.Long> {
+ method public long getLongValue();
+ method public default Long getValue();
+ property public abstract long longValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ field public static final androidx.compose.runtime.MonotonicFrameClock.Key Key;
+ }
+
+ public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.MonotonicFrameClock> {
+ }
+
+ public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
+ method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ }
+
+ @androidx.compose.runtime.InternalComposeApi public final class MovableContent<P> {
+ ctor public MovableContent(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
+ method public kotlin.jvm.functions.Function1<P,kotlin.Unit> getContent();
+ property public final kotlin.jvm.functions.Function1<P,kotlin.Unit> content;
+ }
+
+ public final class MovableContentKt {
+ method public static kotlin.jvm.functions.Function0<kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static <P> kotlin.jvm.functions.Function1<P,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
+ method public static <P1, P2> kotlin.jvm.functions.Function2<P1,P2,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function2<? super P1,? super P2,kotlin.Unit> content);
+ method public static <P1, P2, P3> kotlin.jvm.functions.Function3<P1,P2,P3,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function3<? super P1,? super P2,? super P3,kotlin.Unit> content);
+ method public static <P1, P2, P3, P4> kotlin.jvm.functions.Function4<P1,P2,P3,P4,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function4<? super P1,? super P2,? super P3,? super P4,kotlin.Unit> content);
+ method public static <R> kotlin.jvm.functions.Function1<R,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function1<? super R,kotlin.Unit> content);
+ method public static <R, P> kotlin.jvm.functions.Function2<R,P,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function2<? super R,? super P,kotlin.Unit> content);
+ method public static <R, P1, P2> kotlin.jvm.functions.Function3<R,P1,P2,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function3<? super R,? super P1,? super P2,kotlin.Unit> content);
+ method public static <R, P1, P2, P3> kotlin.jvm.functions.Function4<R,P1,P2,P3,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function4<? super R,? super P1,? super P2,? super P3,kotlin.Unit> content);
+ }
+
+ @androidx.compose.runtime.InternalComposeApi public final class MovableContentState {
+ }
+
+ @androidx.compose.runtime.InternalComposeApi public final class MovableContentStateReference {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableDoubleState extends androidx.compose.runtime.DoubleState androidx.compose.runtime.MutableState<java.lang.Double> {
+ method public void setDoubleValue(double);
+ method public default void setValue(double);
+ property public abstract double doubleValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableFloatState extends androidx.compose.runtime.FloatState androidx.compose.runtime.MutableState<java.lang.Float> {
+ method public void setFloatValue(float);
+ method public default void setValue(float);
+ property public abstract float floatValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableIntState extends androidx.compose.runtime.IntState androidx.compose.runtime.MutableState<java.lang.Integer> {
+ method public void setIntValue(int);
+ method public default void setValue(int);
+ property public abstract int intValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableLongState extends androidx.compose.runtime.LongState androidx.compose.runtime.MutableState<java.lang.Long> {
+ method public void setLongValue(long);
+ method public default void setValue(long);
+ property public abstract long longValue;
+ property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
+ }
+
+ @androidx.compose.runtime.Stable public interface MutableState<T> extends androidx.compose.runtime.State<T> {
+ method public operator T component1();
+ method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
+ method public void setValue(T!);
+ property public abstract T value;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FILE}) public @interface NoLiveLiterals {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface NonRestartableComposable {
+ }
+
+ public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+ ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
+ method public boolean isPaused();
+ method public void pause();
+ method public void resume();
+ method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ property public final boolean isPaused;
+ }
+
+ public final class PrimitiveSnapshotStateKt {
+ method public static inline operator float getValue(androidx.compose.runtime.FloatState, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableFloatState mutableFloatStateOf(float value);
+ method public static inline operator void setValue(androidx.compose.runtime.MutableFloatState, Object? thisObj, kotlin.reflect.KProperty<?> property, float value);
+ }
+
+ public interface ProduceStateScope<T> extends androidx.compose.runtime.MutableState<T> kotlinx.coroutines.CoroutineScope {
+ method public suspend Object? awaitDispose(kotlin.jvm.functions.Function0<kotlin.Unit> onDispose, kotlin.coroutines.Continuation<?>);
+ }
+
+ @androidx.compose.runtime.Stable public abstract class ProvidableCompositionLocal<T> extends androidx.compose.runtime.CompositionLocal<T> {
+ method public final infix androidx.compose.runtime.ProvidedValue<T> provides(T value);
+ method public final infix androidx.compose.runtime.ProvidedValue<T> providesDefault(T value);
+ }
+
+ public final class ProvidedValue<T> {
+ method public boolean getCanOverride();
+ method public androidx.compose.runtime.CompositionLocal<T> getCompositionLocal();
+ method public T getValue();
+ property public final boolean canOverride;
+ property public final androidx.compose.runtime.CompositionLocal<T> compositionLocal;
+ property public final T value;
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ReadOnlyComposable {
+ }
+
+ public interface RecomposeScope {
+ method public void invalidate();
+ }
+
+ public final class Recomposer extends androidx.compose.runtime.CompositionContext {
+ ctor public Recomposer(kotlin.coroutines.CoroutineContext effectCoroutineContext);
+ method public androidx.compose.runtime.RecomposerInfo asRecomposerInfo();
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void cancel();
+ method public void close();
+ method public long getChangeCount();
+ method public kotlinx.coroutines.flow.StateFlow<androidx.compose.runtime.Recomposer.State> getCurrentState();
+ method public kotlin.coroutines.CoroutineContext getEffectCoroutineContext();
+ method public boolean getHasPendingWork();
+ method @Deprecated public kotlinx.coroutines.flow.Flow<androidx.compose.runtime.Recomposer.State> getState();
+ method public suspend Object? join(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void pauseCompositionFrameClock();
+ method public void resumeCompositionFrameClock();
+ method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @androidx.compose.runtime.ExperimentalComposeApi public suspend Object? runRecomposeConcurrentlyAndApplyChanges(kotlin.coroutines.CoroutineContext recomposeCoroutineContext, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final long changeCount;
+ property public final kotlinx.coroutines.flow.StateFlow<androidx.compose.runtime.Recomposer.State> currentState;
+ property public kotlin.coroutines.CoroutineContext effectCoroutineContext;
+ property public final boolean hasPendingWork;
+ property @Deprecated public final kotlinx.coroutines.flow.Flow<androidx.compose.runtime.Recomposer.State> state;
+ field public static final androidx.compose.runtime.Recomposer.Companion Companion;
+ }
+
+ public static final class Recomposer.Companion {
+ method public kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.compose.runtime.RecomposerInfo>> getRunningRecomposers();
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.compose.runtime.RecomposerInfo>> runningRecomposers;
+ }
+
+ public enum Recomposer.State {
+ method public static androidx.compose.runtime.Recomposer.State valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.runtime.Recomposer.State[] values();
+ enum_constant public static final androidx.compose.runtime.Recomposer.State Idle;
+ enum_constant public static final androidx.compose.runtime.Recomposer.State Inactive;
+ enum_constant public static final androidx.compose.runtime.Recomposer.State InactivePendingWork;
+ enum_constant public static final androidx.compose.runtime.Recomposer.State PendingWork;
+ enum_constant public static final androidx.compose.runtime.Recomposer.State ShutDown;
+ enum_constant public static final androidx.compose.runtime.Recomposer.State ShuttingDown;
+ }
+
+ public interface RecomposerInfo {
+ method public long getChangeCount();
+ method public boolean getHasPendingWork();
+ method public kotlinx.coroutines.flow.Flow<androidx.compose.runtime.Recomposer.State> getState();
+ property public abstract long changeCount;
+ property public abstract boolean hasPendingWork;
+ property public abstract kotlinx.coroutines.flow.Flow<androidx.compose.runtime.Recomposer.State> state;
+ }
+
+ public final class RecomposerKt {
+ method public static suspend <R> Object? withRunningRecomposer(kotlin.jvm.functions.Function3<? super kotlinx.coroutines.CoroutineScope,? super androidx.compose.runtime.Recomposer,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
+ }
+
+ public interface RememberObserver {
+ method public void onAbandoned();
+ method public void onForgotten();
+ method public void onRemembered();
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi public interface ScopeUpdateScope {
+ method public void updateScope(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.Composer,? super java.lang.Integer,kotlin.Unit> block);
+ }
+
+ @kotlin.jvm.JvmInline public final value class SkippableUpdater<T> {
+ ctor public SkippableUpdater(@kotlin.PublishedApi androidx.compose.runtime.Composer composer);
+ method public inline void update(kotlin.jvm.functions.Function1<? super androidx.compose.runtime.Updater<T>,kotlin.Unit> block);
+ }
+
+ public final class SnapshotDoubleStateKt {
+ method public static inline operator double getValue(androidx.compose.runtime.DoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableDoubleState mutableDoubleStateOf(double value);
+ method public static inline operator void setValue(androidx.compose.runtime.MutableDoubleState, Object? thisObj, kotlin.reflect.KProperty<?> property, double value);
+ }
+
+ public final class SnapshotIntStateKt {
+ method public static inline operator int getValue(androidx.compose.runtime.IntState, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableIntState mutableIntStateOf(int value);
+ method public static inline operator void setValue(androidx.compose.runtime.MutableIntState, Object? thisObj, kotlin.reflect.KProperty<?> property, int value);
+ }
+
+ public final class SnapshotLongStateKt {
+ method public static inline operator long getValue(androidx.compose.runtime.LongState, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static androidx.compose.runtime.MutableLongState mutableLongStateOf(long value);
+ method public static inline operator void setValue(androidx.compose.runtime.MutableLongState, Object? thisObj, kotlin.reflect.KProperty<?> property, long value);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface SnapshotMutationPolicy<T> {
+ method public boolean equivalent(T a, T b);
+ method public default T? merge(T previous, T current, T applied);
+ }
+
+ public final class SnapshotStateExtensionsKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.runtime.DoubleState asDoubleState(androidx.compose.runtime.State<java.lang.Double>);
+ method @androidx.compose.runtime.Stable public static androidx.compose.runtime.FloatState asFloatState(androidx.compose.runtime.State<java.lang.Float>);
+ method @androidx.compose.runtime.Stable public static androidx.compose.runtime.IntState asIntState(androidx.compose.runtime.State<java.lang.Integer>);
+ method @androidx.compose.runtime.Stable public static androidx.compose.runtime.LongState asLongState(androidx.compose.runtime.State<java.lang.Long>);
+ }
+
+ public final class SnapshotStateKt {
+ method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R initial, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
+ method public static inline operator <T> T getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
+ method @androidx.compose.runtime.snapshots.StateFactoryMarker public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
+ method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
+ method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T newValue);
+ method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T value);
+ method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
+ method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
+ method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
+ method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
+ }
+
+ @androidx.compose.runtime.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface Stable {
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public @interface StableMarker {
+ }
+
+ @androidx.compose.runtime.Stable public interface State<T> {
+ method public T getValue();
+ property public abstract T value;
+ }
+
+ @kotlin.jvm.JvmInline public final value class Updater<T> {
+ ctor public Updater(@kotlin.PublishedApi androidx.compose.runtime.Composer composer);
+ method public void init(kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+ method public void reconcile(kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+ method public inline void set(int value, kotlin.jvm.functions.Function2<? super T,? super java.lang.Integer,kotlin.Unit> block);
+ method public <V> void set(V value, kotlin.jvm.functions.Function2<? super T,? super V,kotlin.Unit> block);
+ method public inline void update(int value, kotlin.jvm.functions.Function2<? super T,? super java.lang.Integer,kotlin.Unit> block);
+ method public <V> void update(V value, kotlin.jvm.functions.Function2<? super T,? super V,kotlin.Unit> block);
+ }
+
+}
+
+package androidx.compose.runtime.collection {
+
+ public final class MutableVector<T> implements java.util.RandomAccess {
+ method public void add(int index, T element);
+ method public boolean add(T element);
+ method public inline boolean addAll(androidx.compose.runtime.collection.MutableVector<T> elements);
+ method public boolean addAll(int index, androidx.compose.runtime.collection.MutableVector<T> elements);
+ method public boolean addAll(int index, java.util.Collection<? extends T> elements);
+ method public boolean addAll(int index, java.util.List<? extends T> elements);
+ method public boolean addAll(java.util.Collection<? extends T> elements);
+ method public inline boolean addAll(java.util.List<? extends T> elements);
+ method public boolean addAll(T![] elements);
+ method public inline boolean any(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public java.util.List<T> asMutableList();
+ method public void clear();
+ method public operator boolean contains(T element);
+ method public boolean containsAll(androidx.compose.runtime.collection.MutableVector<T> elements);
+ method public boolean containsAll(java.util.Collection<? extends T> elements);
+ method public boolean containsAll(java.util.List<? extends T> elements);
+ method public boolean contentEquals(androidx.compose.runtime.collection.MutableVector<T> other);
+ method public void ensureCapacity(int capacity);
+ method public T first();
+ method public inline T first(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public inline T? firstOrNull();
+ method public inline T? firstOrNull(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public inline <R> R fold(R initial, kotlin.jvm.functions.Function2<? super R,? super T,? extends R> operation);
+ method public inline <R> R foldIndexed(R initial, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super R,? super T,? extends R> operation);
+ method public inline <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super T,? super R,? extends R> operation);
+ method public inline <R> R foldRightIndexed(R initial, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super T,? super R,? extends R> operation);
+ method public inline void forEach(kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+ method public inline void forEachIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> block);
+ method public inline void forEachReversed(kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+ method public inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> block);
+ method public inline operator T get(int index);
+ method public inline kotlin.ranges.IntRange getIndices();
+ method public inline int getLastIndex();
+ method public int getSize();
+ method public int indexOf(T element);
+ method public inline int indexOfFirst(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public inline int indexOfLast(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public boolean isEmpty();
+ method public boolean isNotEmpty();
+ method public T last();
+ method public inline T last(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public int lastIndexOf(T element);
+ method public inline T? lastOrNull();
+ method public inline T? lastOrNull(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public inline <reified R> R![] map(kotlin.jvm.functions.Function1<? super T,? extends R> transform);
+ method public inline <reified R> R![] mapIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,? extends R> transform);
+ method public inline <reified R> androidx.compose.runtime.collection.MutableVector<R> mapIndexedNotNull(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,? extends R> transform);
+ method public inline <reified R> androidx.compose.runtime.collection.MutableVector<R> mapNotNull(kotlin.jvm.functions.Function1<? super T,? extends R> transform);
+ method public inline operator void minusAssign(T element);
+ method public inline operator void plusAssign(T element);
+ method public boolean remove(T element);
+ method public boolean removeAll(androidx.compose.runtime.collection.MutableVector<T> elements);
+ method public boolean removeAll(java.util.Collection<? extends T> elements);
+ method public boolean removeAll(java.util.List<? extends T> elements);
+ method public T removeAt(int index);
+ method public void removeRange(int start, int end);
+ method public boolean retainAll(java.util.Collection<? extends T> elements);
+ method public inline boolean reversedAny(kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public operator T set(int index, T element);
+ method public void sortWith(java.util.Comparator<T> comparator);
+ method public inline int sumBy(kotlin.jvm.functions.Function1<? super T,java.lang.Integer> selector);
+ property public final inline kotlin.ranges.IntRange indices;
+ property public final inline int lastIndex;
+ property public final int size;
+ }
+
+ public final class MutableVectorKt {
+ method public static inline <reified T> androidx.compose.runtime.collection.MutableVector<T> MutableVector(optional int capacity);
+ method public static inline <reified T> androidx.compose.runtime.collection.MutableVector<T> MutableVector(int size, kotlin.jvm.functions.Function1<? super java.lang.Integer,? extends T> init);
+ method public static inline <reified T> androidx.compose.runtime.collection.MutableVector<T> mutableVectorOf();
+ method public static inline <reified T> androidx.compose.runtime.collection.MutableVector<T> mutableVectorOf(T?... elements);
+ }
+
+}
+
+package androidx.compose.runtime.internal {
+
+ @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public interface ComposableLambda extends kotlin.jvm.functions.Function2<androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function10<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function11<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function13<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function14<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function15<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function16<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function17<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function18<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function19<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function20<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function21<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function3<java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function4<java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function5<java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function6<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function7<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function8<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> kotlin.jvm.functions.Function9<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,androidx.compose.runtime.Composer,java.lang.Integer,java.lang.Object> {
+ }
+
+ public final class ComposableLambdaKt {
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, Object block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda composableLambdaInstance(int key, boolean tracked, Object block);
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public interface ComposableLambdaN extends kotlin.jvm.functions.FunctionN<java.lang.Object> {
+ }
+
+ public final class ComposableLambdaN_jvmKt {
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambdaN composableLambdaN(androidx.compose.runtime.Composer composer, int key, boolean tracked, int arity, Object block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambdaN composableLambdaNInstance(int key, boolean tracked, int arity, Object block);
+ }
+
+ @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface Decoy {
+ method public abstract String[] signature();
+ method public abstract String targetName();
+ property public abstract String[] signature;
+ property public abstract String targetName;
+ }
+
+ @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface DecoyImplementation {
+ method public abstract long id();
+ method public abstract String name();
+ property public abstract long id;
+ property public abstract String name;
+ }
+
+ public final class DecoyKt {
+ method @androidx.compose.runtime.ComposeCompilerApi public static Void illegalDecoyCallException(String fName);
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface FunctionKeyMeta {
+ method public abstract int endOffset();
+ method public abstract int key();
+ method public abstract int startOffset();
+ property public abstract int endOffset;
+ property public abstract int key;
+ property public abstract int startOffset;
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public static @interface FunctionKeyMeta.Container {
+ method public abstract androidx.compose.runtime.internal.FunctionKeyMeta[] value();
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface FunctionKeyMetaClass {
+ method public abstract String file();
+ property public abstract String file;
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface LiveLiteralFileInfo {
+ method public abstract String file();
+ property public abstract String file;
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface LiveLiteralInfo {
+ method public abstract String key();
+ method public abstract int offset();
+ property public abstract String key;
+ property public abstract int offset;
+ }
+
+ public final class LiveLiteralKt {
+ method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
+ method public static boolean isLiveLiteralsEnabled();
+ method @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T value);
+ method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
+ property public static final boolean isLiveLiteralsEnabled;
+ }
+
+ @androidx.compose.runtime.ComposeCompilerApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface StabilityInferred {
+ method public abstract int parameters();
+ property public abstract int parameters;
+ }
+
+}
+
+package androidx.compose.runtime.reflect {
+
+ public final class ComposableMethod {
+ method public java.lang.reflect.Method asMethod();
+ method public int getParameterCount();
+ method public Class<?>![] getParameterTypes();
+ method public java.lang.reflect.Parameter![] getParameters();
+ method public operator Object? invoke(androidx.compose.runtime.Composer composer, Object? instance, java.lang.Object?... args);
+ property public final int parameterCount;
+ property public final Class<?>![] parameterTypes;
+ property public final java.lang.reflect.Parameter![] parameters;
+ }
+
+ public final class ComposableMethodKt {
+ method public static androidx.compose.runtime.reflect.ComposableMethod? asComposableMethod(java.lang.reflect.Method);
+ method @kotlin.jvm.Throws(exceptionClasses=NoSuchMethodException::class) public static androidx.compose.runtime.reflect.ComposableMethod getDeclaredComposableMethod(Class<?>, String methodName, Class<?>... args) throws java.lang.NoSuchMethodException;
+ }
+
+}
+
+package androidx.compose.runtime.snapshots {
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.PROPERTY) public @interface AutoboxingStateValueProperty {
+ method public abstract String preferredPropertyName();
+ property public abstract String preferredPropertyName;
+ }
+
+ public class MutableSnapshot extends androidx.compose.runtime.snapshots.Snapshot {
+ method public androidx.compose.runtime.snapshots.SnapshotApplyResult apply();
+ method public kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? getReadObserver();
+ method public boolean getReadOnly();
+ method public androidx.compose.runtime.snapshots.Snapshot getRoot();
+ method public kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? getWriteObserver();
+ method public boolean hasPendingChanges();
+ method public androidx.compose.runtime.snapshots.MutableSnapshot takeNestedMutableSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver);
+ method public androidx.compose.runtime.snapshots.Snapshot takeNestedSnapshot(kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver);
+ property public kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver;
+ property public boolean readOnly;
+ property public androidx.compose.runtime.snapshots.Snapshot root;
+ property public kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver;
+ }
+
+ public fun interface ObserverHandle {
+ method public void dispose();
+ }
+
+ public abstract sealed class Snapshot {
+ method public void dispose();
+ method public final inline <T> T enter(kotlin.jvm.functions.Function0<? extends T> block);
+ method public int getId();
+ method public abstract boolean getReadOnly();
+ method public abstract androidx.compose.runtime.snapshots.Snapshot getRoot();
+ method public abstract boolean hasPendingChanges();
+ method public abstract androidx.compose.runtime.snapshots.Snapshot takeNestedSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver);
+ method @androidx.compose.runtime.ExperimentalComposeApi public final androidx.compose.runtime.snapshots.Snapshot? unsafeEnter();
+ method @androidx.compose.runtime.ExperimentalComposeApi public final void unsafeLeave(androidx.compose.runtime.snapshots.Snapshot? oldSnapshot);
+ property public int id;
+ property public abstract boolean readOnly;
+ property public abstract androidx.compose.runtime.snapshots.Snapshot root;
+ field public static final androidx.compose.runtime.snapshots.Snapshot.Companion Companion;
+ }
+
+ public static final class Snapshot.Companion {
+ method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
+ method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
+ method public boolean isApplyObserverNotificationPending();
+ method public void notifyObjectsInitialized();
+ method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
+ method @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
+ method public androidx.compose.runtime.snapshots.ObserverHandle registerApplyObserver(kotlin.jvm.functions.Function2<? super java.util.Set<?>,? super androidx.compose.runtime.snapshots.Snapshot,kotlin.Unit> observer);
+ method public androidx.compose.runtime.snapshots.ObserverHandle registerGlobalWriteObserver(kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit> observer);
+ method public void sendApplyNotifications();
+ method public androidx.compose.runtime.snapshots.MutableSnapshot takeMutableSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver);
+ method public androidx.compose.runtime.snapshots.Snapshot takeSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver);
+ method public inline <R> R withMutableSnapshot(kotlin.jvm.functions.Function0<? extends R> block);
+ method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
+ property public final androidx.compose.runtime.snapshots.Snapshot current;
+ property public final boolean isApplyObserverNotificationPending;
+ }
+
+ public final class SnapshotApplyConflictException extends java.lang.Exception {
+ ctor public SnapshotApplyConflictException(androidx.compose.runtime.snapshots.Snapshot snapshot);
+ method public androidx.compose.runtime.snapshots.Snapshot getSnapshot();
+ property public final androidx.compose.runtime.snapshots.Snapshot snapshot;
+ }
+
+ public abstract sealed class SnapshotApplyResult {
+ method public abstract void check();
+ method public abstract boolean getSucceeded();
+ property public abstract boolean succeeded;
+ }
+
+ public static final class SnapshotApplyResult.Failure extends androidx.compose.runtime.snapshots.SnapshotApplyResult {
+ ctor public SnapshotApplyResult.Failure(androidx.compose.runtime.snapshots.Snapshot snapshot);
+ method public void check();
+ method public androidx.compose.runtime.snapshots.Snapshot getSnapshot();
+ method public boolean getSucceeded();
+ property public final androidx.compose.runtime.snapshots.Snapshot snapshot;
+ property public boolean succeeded;
+ }
+
+ public static final class SnapshotApplyResult.Success extends androidx.compose.runtime.snapshots.SnapshotApplyResult {
+ method public void check();
+ method public boolean getSucceeded();
+ property public boolean succeeded;
+ field public static final androidx.compose.runtime.snapshots.SnapshotApplyResult.Success INSTANCE;
+ }
+
+ @androidx.compose.runtime.ExperimentalComposeApi public interface SnapshotContextElement extends kotlin.coroutines.CoroutineContext.Element {
+ field public static final androidx.compose.runtime.snapshots.SnapshotContextElement.Key Key;
+ }
+
+ public static final class SnapshotContextElement.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.snapshots.SnapshotContextElement> {
+ }
+
+ public final class SnapshotContextElementKt {
+ method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.snapshots.SnapshotContextElement asContextElement(androidx.compose.runtime.snapshots.Snapshot);
+ }
+
+ public final class SnapshotKt {
+ method public static <T extends androidx.compose.runtime.snapshots.StateRecord> T readable(T, androidx.compose.runtime.snapshots.StateObject state);
+ method public static <T extends androidx.compose.runtime.snapshots.StateRecord> T readable(T, androidx.compose.runtime.snapshots.StateObject state, androidx.compose.runtime.snapshots.Snapshot snapshot);
+ method public static inline <T extends androidx.compose.runtime.snapshots.StateRecord, R> R withCurrent(T, kotlin.jvm.functions.Function1<? super T,? extends R> block);
+ method public static inline <T extends androidx.compose.runtime.snapshots.StateRecord, R> R writable(T, androidx.compose.runtime.snapshots.StateObject state, androidx.compose.runtime.snapshots.Snapshot snapshot, kotlin.jvm.functions.Function1<? super T,? extends R> block);
+ method public static inline <T extends androidx.compose.runtime.snapshots.StateRecord, R> R writable(T, androidx.compose.runtime.snapshots.StateObject state, kotlin.jvm.functions.Function1<? super T,? extends R> block);
+ }
+
+ public interface SnapshotMutableState<T> extends androidx.compose.runtime.MutableState<T> {
+ method public androidx.compose.runtime.SnapshotMutationPolicy<T> getPolicy();
+ property public abstract androidx.compose.runtime.SnapshotMutationPolicy<T> policy;
+ }
+
+ @androidx.compose.runtime.Stable public final class SnapshotStateList<T> implements kotlin.jvm.internal.markers.KMutableList java.util.List<T> androidx.compose.runtime.snapshots.StateObject {
+ ctor public SnapshotStateList();
+ method public void add(int index, T element);
+ method public boolean add(T element);
+ method public boolean addAll(int index, java.util.Collection<? extends T> elements);
+ method public boolean addAll(java.util.Collection<? extends T> elements);
+ method public void clear();
+ method public boolean contains(T element);
+ method public boolean containsAll(java.util.Collection<E!> elements);
+ method public T get(int index);
+ method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
+ method public int getSize();
+ method public int indexOf(T element);
+ method public boolean isEmpty();
+ method public java.util.Iterator<T> iterator();
+ method public int lastIndexOf(T element);
+ method public java.util.ListIterator<T> listIterator();
+ method public java.util.ListIterator<T> listIterator(int index);
+ method public void prependStateRecord(androidx.compose.runtime.snapshots.StateRecord value);
+ method public boolean remove(T element);
+ method public boolean removeAll(java.util.Collection<E!> elements);
+ method public T removeAt(int index);
+ method public void removeRange(int fromIndex, int toIndex);
+ method public boolean retainAll(java.util.Collection<E!> elements);
+ method public T set(int index, T element);
+ method public java.util.List<T> subList(int fromIndex, int toIndex);
+ method public java.util.List<T> toList();
+ property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
+ property public int size;
+ }
+
+ @androidx.compose.runtime.Stable public final class SnapshotStateMap<K, V> implements kotlin.jvm.internal.markers.KMutableMap java.util.Map<K,V> androidx.compose.runtime.snapshots.StateObject {
+ ctor public SnapshotStateMap();
+ method public void clear();
+ method public boolean containsKey(K key);
+ method public boolean containsValue(V value);
+ method public V? get(Object key);
+ method public java.util.Set<java.util.Map.Entry<K,V>> getEntries();
+ method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
+ method public java.util.Set<K> getKeys();
+ method public int getSize();
+ method public java.util.Collection<V> getValues();
+ method public boolean isEmpty();
+ method public void prependStateRecord(androidx.compose.runtime.snapshots.StateRecord value);
+ method public V? put(K key, V value);
+ method public void putAll(java.util.Map<? extends K,? extends V> from);
+ method public V? remove(Object key);
+ method public java.util.Map<K,V> toMap();
+ property public java.util.Set<java.util.Map.Entry<K,V>> entries;
+ property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
+ property public java.util.Set<K> keys;
+ property public int size;
+ property public java.util.Collection<V> values;
+ }
+
+ public final class SnapshotStateObserver {
+ ctor public SnapshotStateObserver(kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> onChangedExecutor);
+ method public void clear();
+ method public void clear(Object scope);
+ method public void clearIf(kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> predicate);
+ method @org.jetbrains.annotations.TestOnly public void notifyChanges(java.util.Set<?> changes, androidx.compose.runtime.snapshots.Snapshot snapshot);
+ method public <T> void observeReads(T scope, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onValueChangedForScope, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public void start();
+ method public void stop();
+ method @Deprecated public void withNoObservations(kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface StateFactoryMarker {
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface StateObject {
+ method public androidx.compose.runtime.snapshots.StateRecord getFirstStateRecord();
+ method public default androidx.compose.runtime.snapshots.StateRecord? mergeRecords(androidx.compose.runtime.snapshots.StateRecord previous, androidx.compose.runtime.snapshots.StateRecord current, androidx.compose.runtime.snapshots.StateRecord applied);
+ method public void prependStateRecord(androidx.compose.runtime.snapshots.StateRecord value);
+ property public abstract androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
+ }
+
+ public abstract class StateRecord {
+ ctor public StateRecord();
+ method public abstract void assign(androidx.compose.runtime.snapshots.StateRecord value);
+ method public abstract androidx.compose.runtime.snapshots.StateRecord create();
+ }
+
+}
+
+package androidx.compose.runtime.tooling {
+
+ public interface CompositionData {
+ method public default androidx.compose.runtime.tooling.CompositionGroup? find(Object identityToFind);
+ method public Iterable<androidx.compose.runtime.tooling.CompositionGroup> getCompositionGroups();
+ method public boolean isEmpty();
+ property public abstract Iterable<androidx.compose.runtime.tooling.CompositionGroup> compositionGroups;
+ property public abstract boolean isEmpty;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface CompositionGroup extends androidx.compose.runtime.tooling.CompositionData {
+ method public Iterable<java.lang.Object> getData();
+ method public default int getGroupSize();
+ method public default Object? getIdentity();
+ method public Object getKey();
+ method public Object? getNode();
+ method public default int getSlotsSize();
+ method public String? getSourceInfo();
+ property public abstract Iterable<java.lang.Object> data;
+ property public default int groupSize;
+ property public default Object? identity;
+ property public abstract Object key;
+ property public abstract Object? node;
+ property public default int slotsSize;
+ property public abstract String? sourceInfo;
+ }
+
+ public final class InspectionTablesKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.util.Set<androidx.compose.runtime.tooling.CompositionData>> getLocalInspectionTables();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.util.Set<androidx.compose.runtime.tooling.CompositionData>> LocalInspectionTables;
+ }
+
+}
+
diff --git a/compose/runtime/runtime/api/restricted_1.5.0-beta01.txt b/compose/runtime/runtime/api/restricted_1.5.0-beta01.txt
index 6cfdfcb..fd14be5 100644
--- a/compose/runtime/runtime/api/restricted_1.5.0-beta01.txt
+++ b/compose/runtime/runtime/api/restricted_1.5.0-beta01.txt
@@ -51,11 +51,6 @@
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface Composable {
}
- @androidx.compose.runtime.InternalComposeApi @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ComposableInferredTarget {
- method public abstract String scheme();
- property public abstract String scheme;
- }
-
@kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface ComposableOpenTarget {
method public abstract int index();
property public abstract int index;
@@ -109,7 +104,6 @@
public sealed interface Composer {
method @androidx.compose.runtime.ComposeCompilerApi public <V, T> void apply(V value, kotlin.jvm.functions.Function2<? super T,? super V,kotlin.Unit> block);
- method @androidx.compose.runtime.InternalComposeApi public androidx.compose.runtime.CompositionContext buildContext();
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(boolean value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(byte value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(char value);
@@ -121,7 +115,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(short value);
method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
method public void collectParameterInformation();
- method @androidx.compose.runtime.InternalComposeApi public <T> T consume(androidx.compose.runtime.CompositionLocal<T> key);
method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
@@ -130,7 +123,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void endNode();
- method @androidx.compose.runtime.InternalComposeApi public void endProviders();
method @androidx.compose.runtime.ComposeCompilerApi public void endReplaceableGroup();
method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.ScopeUpdateScope? endRestartGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void endReusableGroup();
@@ -147,11 +139,7 @@
method public androidx.compose.runtime.RecomposeScope? getRecomposeScope();
method public Object? getRecomposeScopeIdentity();
method public boolean getSkipping();
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(androidx.compose.runtime.MovableContent<?> value, Object? parameter);
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContentReferences(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
- method @androidx.compose.runtime.InternalComposeApi public void recordSideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
- method @androidx.compose.runtime.InternalComposeApi public void recordUsed(androidx.compose.runtime.RecomposeScope scope);
method @androidx.compose.runtime.ComposeCompilerApi public Object? rememberedValue();
method @androidx.compose.runtime.ComposeCompilerApi public void skipCurrentGroup();
method @androidx.compose.runtime.ComposeCompilerApi public void skipToGroupEnd();
@@ -161,7 +149,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void startDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void startMovableGroup(int key, Object? dataKey);
method @androidx.compose.runtime.ComposeCompilerApi public void startNode();
- method @androidx.compose.runtime.InternalComposeApi public void startProviders(androidx.compose.runtime.ProvidedValue<?>![] values);
method @androidx.compose.runtime.ComposeCompilerApi public void startReplaceableGroup(int key);
method @androidx.compose.runtime.ComposeCompilerApi public androidx.compose.runtime.Composer startRestartGroup(int key);
method @androidx.compose.runtime.ComposeCompilerApi public void startReusableGroup(int key, Object? dataKey);
@@ -169,7 +156,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public void updateRememberedValue(Object? value);
method @androidx.compose.runtime.ComposeCompilerApi public void useNode();
property public abstract androidx.compose.runtime.Applier<?> applier;
- property @androidx.compose.runtime.InternalComposeApi @org.jetbrains.annotations.TestOnly public abstract kotlin.coroutines.CoroutineContext applyCoroutineContext;
property @org.jetbrains.annotations.TestOnly public abstract androidx.compose.runtime.ControlledComposition composition;
property public abstract androidx.compose.runtime.tooling.CompositionData compositionData;
property public abstract int compoundKeyHash;
@@ -185,7 +171,6 @@
public static final class Composer.Companion {
method public Object getEmpty();
- method @androidx.compose.runtime.InternalComposeTracingApi public void setTracer(androidx.compose.runtime.CompositionTracer tracer);
property public final Object Empty;
}
@@ -229,10 +214,7 @@
public final class CompositionKt {
method public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition Composition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
method @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent);
- method @androidx.compose.runtime.ExperimentalComposeApi @org.jetbrains.annotations.TestOnly public static androidx.compose.runtime.ControlledComposition ControlledComposition(androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionContext parent, kotlin.coroutines.CoroutineContext recomposeCoroutineContext);
- method @androidx.compose.runtime.ExperimentalComposeApi public static kotlin.coroutines.CoroutineContext getRecomposeCoroutineContext(androidx.compose.runtime.ControlledComposition);
}
@androidx.compose.runtime.Stable public abstract sealed class CompositionLocal<T> {
@@ -269,21 +251,13 @@
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
}
- @androidx.compose.runtime.InternalComposeTracingApi public interface CompositionTracer {
- method public boolean isTraceInProgress();
- method public void traceEventEnd();
- method public void traceEventStart(int key, int dirty1, int dirty2, String info);
- }
-
public sealed interface ControlledComposition extends androidx.compose.runtime.Composition {
method public void applyChanges();
method public void applyLateChanges();
method public void changesApplied();
method public void composeContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
method public <R> R delegateInvalidations(androidx.compose.runtime.ControlledComposition? to, int groupIndex, kotlin.jvm.functions.Function0<? extends R> block);
- method @androidx.compose.runtime.InternalComposeApi public void disposeUnusedMovableContent(androidx.compose.runtime.MovableContentState state);
method public boolean getHasPendingChanges();
- method @androidx.compose.runtime.InternalComposeApi public void insertMovableContent(java.util.List<kotlin.Pair<androidx.compose.runtime.MovableContentStateReference,androidx.compose.runtime.MovableContentStateReference>> references);
method public void invalidateAll();
method public boolean isComposing();
method public boolean observesAnyOf(java.util.Set<?> values);
@@ -292,7 +266,6 @@
method public void recordModificationsOf(java.util.Set<?> values);
method public void recordReadOf(Object value);
method public void recordWriteOf(Object value);
- method @androidx.compose.runtime.InternalComposeApi public void verifyConsistent();
property public abstract boolean hasPendingChanges;
property public abstract boolean isComposing;
}
@@ -336,9 +309,6 @@
method @kotlin.PublishedApi internal static inline <R> R synchronized(Object lock, kotlin.jvm.functions.Function0<? extends R> block);
}
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is an experimental API for Compose and is likely to change before becoming " + "stable.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExperimentalComposeApi {
- }
-
@kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface ExplicitGroupsComposable {
}
@@ -359,12 +329,6 @@
property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
}
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API for Compose modules that may change frequently " + "and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalComposeApi {
- }
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeTracingApi {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LongState extends androidx.compose.runtime.State<java.lang.Long> {
method public long getLongValue();
method public default Long getValue();
@@ -389,12 +353,6 @@
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
}
- @androidx.compose.runtime.InternalComposeApi public final class MovableContent<P> {
- ctor public MovableContent(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
- method public kotlin.jvm.functions.Function1<P,kotlin.Unit> getContent();
- property public final kotlin.jvm.functions.Function1<P,kotlin.Unit> content;
- }
-
public final class MovableContentKt {
method public static kotlin.jvm.functions.Function0<kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function0<kotlin.Unit> content);
method public static <P> kotlin.jvm.functions.Function1<P,kotlin.Unit> movableContentOf(kotlin.jvm.functions.Function1<? super P,kotlin.Unit> content);
@@ -407,12 +365,6 @@
method public static <R, P1, P2, P3> kotlin.jvm.functions.Function4<R,P1,P2,P3,kotlin.Unit> movableContentWithReceiverOf(kotlin.jvm.functions.Function4<? super R,? super P1,? super P2,? super P3,kotlin.Unit> content);
}
- @androidx.compose.runtime.InternalComposeApi public final class MovableContentState {
- }
-
- @androidx.compose.runtime.InternalComposeApi public final class MovableContentStateReference {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableDoubleState extends androidx.compose.runtime.DoubleState androidx.compose.runtime.MutableState<java.lang.Double> {
method public void setDoubleValue(double);
method public default void setValue(double);
@@ -513,7 +465,6 @@
method public void pauseCompositionFrameClock();
method public void resumeCompositionFrameClock();
method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method @androidx.compose.runtime.ExperimentalComposeApi public suspend Object? runRecomposeConcurrentlyAndApplyChanges(kotlin.coroutines.CoroutineContext recomposeCoroutineContext, kotlin.coroutines.Continuation<? super kotlin.Unit>);
property public final long changeCount;
property public final kotlinx.coroutines.flow.StateFlow<androidx.compose.runtime.Recomposer.State> currentState;
property public kotlin.coroutines.CoroutineContext effectCoroutineContext;
@@ -743,20 +694,6 @@
method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambdaN composableLambdaNInstance(int key, boolean tracked, int arity, Object block);
}
- @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface Decoy {
- method public abstract String[] signature();
- method public abstract String targetName();
- property public abstract String[] signature;
- property public abstract String targetName;
- }
-
- @androidx.compose.runtime.ExperimentalComposeApi @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.CONSTRUCTOR}) public @interface DecoyImplementation {
- method public abstract long id();
- method public abstract String name();
- property public abstract long id;
- property public abstract String name;
- }
-
public final class DecoyKt {
method @androidx.compose.runtime.ComposeCompilerApi public static Void illegalDecoyCallException(String fName);
}
@@ -792,10 +729,7 @@
}
public final class LiveLiteralKt {
- method @androidx.compose.runtime.InternalComposeApi public static void enableLiveLiterals();
method public static boolean isLiveLiteralsEnabled();
- method @androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.InternalComposeApi public static <T> androidx.compose.runtime.State<T> liveLiteral(String key, T value);
- method @androidx.compose.runtime.InternalComposeApi public static void updateLiveLiteralValue(String key, Object? value);
property public static final boolean isLiveLiteralsEnabled;
}
@@ -862,8 +796,6 @@
method @kotlin.PublishedApi internal androidx.compose.runtime.snapshots.Snapshot? makeCurrent();
method @kotlin.PublishedApi internal void restoreCurrent(androidx.compose.runtime.snapshots.Snapshot? snapshot);
method public abstract androidx.compose.runtime.snapshots.Snapshot takeNestedSnapshot(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver);
- method @androidx.compose.runtime.ExperimentalComposeApi public final androidx.compose.runtime.snapshots.Snapshot? unsafeEnter();
- method @androidx.compose.runtime.ExperimentalComposeApi public final void unsafeLeave(androidx.compose.runtime.snapshots.Snapshot? oldSnapshot);
property public int id;
property public abstract boolean readOnly;
property public abstract androidx.compose.runtime.snapshots.Snapshot root;
@@ -877,7 +809,6 @@
method public boolean isApplyObserverNotificationPending();
method public void notifyObjectsInitialized();
method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
- method @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
method public androidx.compose.runtime.snapshots.ObserverHandle registerApplyObserver(kotlin.jvm.functions.Function2<? super java.util.Set<?>,? super androidx.compose.runtime.snapshots.Snapshot,kotlin.Unit> observer);
method public androidx.compose.runtime.snapshots.ObserverHandle registerGlobalWriteObserver(kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit> observer);
method @kotlin.PublishedApi internal androidx.compose.runtime.snapshots.Snapshot? removeCurrent();
@@ -919,17 +850,6 @@
field public static final androidx.compose.runtime.snapshots.SnapshotApplyResult.Success INSTANCE;
}
- @androidx.compose.runtime.ExperimentalComposeApi public interface SnapshotContextElement extends kotlin.coroutines.CoroutineContext.Element {
- field public static final androidx.compose.runtime.snapshots.SnapshotContextElement.Key Key;
- }
-
- public static final class SnapshotContextElement.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.snapshots.SnapshotContextElement> {
- }
-
- public final class SnapshotContextElementKt {
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.snapshots.SnapshotContextElement asContextElement(androidx.compose.runtime.snapshots.Snapshot);
- }
-
public final class SnapshotKt {
method @kotlin.PublishedApi internal static <T extends androidx.compose.runtime.snapshots.StateRecord> T current(T r);
method @kotlin.PublishedApi internal static <T extends androidx.compose.runtime.snapshots.StateRecord> T current(T r, androidx.compose.runtime.snapshots.Snapshot snapshot);
diff --git a/compose/ui/ui-android-stubs/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-android-stubs/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..8e22228
--- /dev/null
+++ b/compose/ui/ui-android-stubs/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,78 @@
+// Signature format: 4.0
+package android.view {
+
+ public final class DisplayListCanvas extends android.graphics.Canvas {
+ ctor public DisplayListCanvas();
+ method public void drawRenderNode(android.view.RenderNode);
+ }
+
+ public class RenderNode {
+ method public static android.view.RenderNode create(String?, android.view.View?);
+ method public void destroy();
+ method public void destroyDisplayListData();
+ method public void discardDisplayList();
+ method public void end(android.view.DisplayListCanvas);
+ method public float getAlpha();
+ method public int getAmbientShadowColor();
+ method public float getCameraDistance();
+ method public boolean getClipToOutline();
+ method public float getElevation();
+ method public void getInverseMatrix(android.graphics.Matrix);
+ method public void getMatrix(android.graphics.Matrix);
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getRotationX();
+ method public float getRotationY();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSpotShadowColor();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public float getTranslationZ();
+ method public boolean hasIdentityMatrix();
+ method public boolean hasOverlappingRendering();
+ method public boolean hasShadow();
+ method public boolean isAttached();
+ method public boolean isPivotExplicitlySet();
+ method public boolean isValid();
+ method public boolean offsetLeftAndRight(int);
+ method public boolean offsetTopAndBottom(int);
+ method public void output();
+ method public boolean setAlpha(float);
+ method public boolean setAmbientShadowColor(int);
+ method public boolean setAnimationMatrix(android.graphics.Matrix);
+ method public boolean setBottom(int);
+ method public boolean setCameraDistance(float);
+ method public boolean setClipBounds(android.graphics.Rect?);
+ method public boolean setClipToBounds(boolean);
+ method public boolean setClipToOutline(boolean);
+ method public boolean setElevation(float);
+ method public boolean setHasOverlappingRendering(boolean);
+ method public boolean setLayerPaint(android.graphics.Paint?);
+ method public boolean setLayerType(int);
+ method public boolean setLeft(int);
+ method public boolean setLeftTopRightBottom(int, int, int, int);
+ method public boolean setOutline(android.graphics.Outline?);
+ method public boolean setPivotX(float);
+ method public boolean setPivotY(float);
+ method public boolean setProjectBackwards(boolean);
+ method public boolean setProjectionReceiver(boolean);
+ method public boolean setRevealClip(boolean, float, float, float);
+ method public boolean setRight(int);
+ method public boolean setRotation(float);
+ method public boolean setRotationX(float);
+ method public boolean setRotationY(float);
+ method public boolean setScaleX(float);
+ method public boolean setScaleY(float);
+ method public boolean setSpotShadowColor(int);
+ method public boolean setStaticMatrix(android.graphics.Matrix);
+ method public boolean setTop(int);
+ method public boolean setTranslationX(float);
+ method public boolean setTranslationY(float);
+ method public boolean setTranslationZ(float);
+ method public android.view.DisplayListCanvas start(int, int);
+ }
+
+}
+
diff --git a/compose/ui/ui-geometry/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-geometry/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..5aabfcd
--- /dev/null
+++ b/compose/ui/ui-geometry/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,272 @@
+// Signature format: 4.0
+package androidx.compose.ui.geometry {
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class CornerRadius {
+ method @androidx.compose.runtime.Stable public inline operator float component1();
+ method @androidx.compose.runtime.Stable public inline operator float component2();
+ method public long copy(optional float x, optional float y);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public float getX();
+ method public float getY();
+ method @androidx.compose.runtime.Stable public operator long minus(long other);
+ method @androidx.compose.runtime.Stable public operator long plus(long other);
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ method @androidx.compose.runtime.Stable public operator long unaryMinus();
+ property @androidx.compose.runtime.Stable public final float x;
+ property @androidx.compose.runtime.Stable public final float y;
+ field public static final androidx.compose.ui.geometry.CornerRadius.Companion Companion;
+ }
+
+ public static final class CornerRadius.Companion {
+ method public long getZero();
+ property public final long Zero;
+ }
+
+ public final class CornerRadiusKt {
+ method @androidx.compose.runtime.Stable public static long CornerRadius(float x, optional float y);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ }
+
+ public final class MutableRect {
+ ctor public MutableRect(float left, float top, float right, float bottom);
+ method public operator boolean contains(long offset);
+ method public float getBottom();
+ method public inline float getHeight();
+ method public float getLeft();
+ method public float getRight();
+ method public long getSize();
+ method public float getTop();
+ method public inline float getWidth();
+ method @androidx.compose.runtime.Stable public void intersect(float left, float top, float right, float bottom);
+ method public boolean isEmpty();
+ method public void set(float left, float top, float right, float bottom);
+ method public void setBottom(float);
+ method public void setLeft(float);
+ method public void setRight(float);
+ method public void setTop(float);
+ property public final float bottom;
+ property public final inline float height;
+ property public final boolean isEmpty;
+ property public final float left;
+ property public final float right;
+ property public final long size;
+ property public final float top;
+ property public final inline float width;
+ }
+
+ public final class MutableRectKt {
+ method public static androidx.compose.ui.geometry.Rect toRect(androidx.compose.ui.geometry.MutableRect);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Offset {
+ method @androidx.compose.runtime.Stable public operator float component1();
+ method @androidx.compose.runtime.Stable public operator float component2();
+ method public long copy(optional float x, optional float y);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method @androidx.compose.runtime.Stable public float getDistance();
+ method @androidx.compose.runtime.Stable public float getDistanceSquared();
+ method public float getX();
+ method public float getY();
+ method @androidx.compose.runtime.Stable public boolean isValid();
+ method @androidx.compose.runtime.Stable public operator long minus(long other);
+ method @androidx.compose.runtime.Stable public operator long plus(long other);
+ method @androidx.compose.runtime.Stable public operator long rem(float operand);
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ method @androidx.compose.runtime.Stable public operator long unaryMinus();
+ property @androidx.compose.runtime.Stable public final float x;
+ property @androidx.compose.runtime.Stable public final float y;
+ field public static final androidx.compose.ui.geometry.Offset.Companion Companion;
+ }
+
+ public static final class Offset.Companion {
+ method public long getInfinite();
+ method public long getUnspecified();
+ method public long getZero();
+ property public final long Infinite;
+ property public final long Unspecified;
+ property public final long Zero;
+ }
+
+ public final class OffsetKt {
+ method @androidx.compose.runtime.Stable public static long Offset(float x, float y);
+ method public static boolean isFinite(long);
+ method public static boolean isSpecified(long);
+ method public static boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Offset> block);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Rect {
+ ctor public Rect(@androidx.compose.runtime.Stable float left, @androidx.compose.runtime.Stable float top, @androidx.compose.runtime.Stable float right, @androidx.compose.runtime.Stable float bottom);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public operator boolean contains(long offset);
+ method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
+ method public float getBottom();
+ method public long getBottomCenter();
+ method public long getBottomLeft();
+ method public long getBottomRight();
+ method public long getCenter();
+ method public long getCenterLeft();
+ method public long getCenterRight();
+ method public float getHeight();
+ method public float getLeft();
+ method public float getMaxDimension();
+ method public float getMinDimension();
+ method public float getRight();
+ method public long getSize();
+ method public float getTop();
+ method public long getTopCenter();
+ method public long getTopLeft();
+ method public long getTopRight();
+ method public float getWidth();
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect inflate(float delta);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect intersect(androidx.compose.ui.geometry.Rect other);
+ method public boolean isEmpty();
+ method public boolean isFinite();
+ method public boolean isInfinite();
+ method public boolean overlaps(androidx.compose.ui.geometry.Rect other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect translate(float translateX, float translateY);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect translate(long offset);
+ property public final float bottom;
+ property public final long bottomCenter;
+ property public final long bottomLeft;
+ property public final long bottomRight;
+ property public final long center;
+ property public final long centerLeft;
+ property public final long centerRight;
+ property @androidx.compose.runtime.Stable public final float height;
+ property @androidx.compose.runtime.Stable public final boolean isEmpty;
+ property @androidx.compose.runtime.Stable public final boolean isFinite;
+ property @androidx.compose.runtime.Stable public final boolean isInfinite;
+ property public final float left;
+ property public final float maxDimension;
+ property public final float minDimension;
+ property public final float right;
+ property @androidx.compose.runtime.Stable public final long size;
+ property public final float top;
+ property public final long topCenter;
+ property public final long topLeft;
+ property public final long topRight;
+ property @androidx.compose.runtime.Stable public final float width;
+ field public static final androidx.compose.ui.geometry.Rect.Companion Companion;
+ }
+
+ public static final class Rect.Companion {
+ method public androidx.compose.ui.geometry.Rect getZero();
+ property public final androidx.compose.ui.geometry.Rect Zero;
+ }
+
+ public final class RectKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect Rect(long center, float radius);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect Rect(long offset, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect Rect(long topLeft, long bottomRight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect lerp(androidx.compose.ui.geometry.Rect start, androidx.compose.ui.geometry.Rect stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class RoundRect {
+ ctor public RoundRect(float left, float top, float right, float bottom, optional long topLeftCornerRadius, optional long topRightCornerRadius, optional long bottomRightCornerRadius, optional long bottomLeftCornerRadius);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public long component5-kKHJgLs();
+ method public long component6-kKHJgLs();
+ method public long component7-kKHJgLs();
+ method public long component8-kKHJgLs();
+ method public operator boolean contains(long point);
+ method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
+ method public float getBottom();
+ method public long getBottomLeftCornerRadius();
+ method public long getBottomRightCornerRadius();
+ method public float getHeight();
+ method public float getLeft();
+ method public float getRight();
+ method public float getTop();
+ method public long getTopLeftCornerRadius();
+ method public long getTopRightCornerRadius();
+ method public float getWidth();
+ method public static androidx.compose.ui.geometry.RoundRect getZero();
+ property public static final androidx.compose.ui.geometry.RoundRect Zero;
+ property public final float bottom;
+ property public final long bottomLeftCornerRadius;
+ property public final long bottomRightCornerRadius;
+ property public final float height;
+ property public final float left;
+ property public final float right;
+ property public final float top;
+ property public final long topLeftCornerRadius;
+ property public final long topRightCornerRadius;
+ property public final float width;
+ field public static final androidx.compose.ui.geometry.RoundRect.Companion Companion;
+ }
+
+ public static final class RoundRect.Companion {
+ method public androidx.compose.ui.geometry.RoundRect getZero();
+ property public final androidx.compose.ui.geometry.RoundRect Zero;
+ }
+
+ public final class RoundRectKt {
+ method public static androidx.compose.ui.geometry.RoundRect RoundRect(androidx.compose.ui.geometry.Rect rect, float radiusX, float radiusY);
+ method public static androidx.compose.ui.geometry.RoundRect RoundRect(androidx.compose.ui.geometry.Rect rect, long cornerRadius);
+ method public static androidx.compose.ui.geometry.RoundRect RoundRect(androidx.compose.ui.geometry.Rect rect, optional long topLeft, optional long topRight, optional long bottomRight, optional long bottomLeft);
+ method public static androidx.compose.ui.geometry.RoundRect RoundRect(float left, float top, float right, float bottom, float radiusX, float radiusY);
+ method public static androidx.compose.ui.geometry.RoundRect RoundRect(float left, float top, float right, float bottom, long cornerRadius);
+ method public static androidx.compose.ui.geometry.Rect getBoundingRect(androidx.compose.ui.geometry.RoundRect);
+ method public static long getCenter(androidx.compose.ui.geometry.RoundRect);
+ method public static float getMaxDimension(androidx.compose.ui.geometry.RoundRect);
+ method public static float getMinDimension(androidx.compose.ui.geometry.RoundRect);
+ method public static androidx.compose.ui.geometry.Rect getSafeInnerRect(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isCircle(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isEllipse(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isEmpty(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isFinite(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isRect(androidx.compose.ui.geometry.RoundRect);
+ method public static boolean isSimple(androidx.compose.ui.geometry.RoundRect);
+ method public static androidx.compose.ui.geometry.RoundRect lerp(androidx.compose.ui.geometry.RoundRect start, androidx.compose.ui.geometry.RoundRect stop, float fraction);
+ method public static androidx.compose.ui.geometry.RoundRect translate(androidx.compose.ui.geometry.RoundRect, long offset);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Size {
+ method @androidx.compose.runtime.Stable public inline operator float component1();
+ method @androidx.compose.runtime.Stable public inline operator float component2();
+ method public long copy(optional float width, optional float height);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public float getHeight();
+ method public float getMaxDimension();
+ method public float getMinDimension();
+ method public float getWidth();
+ method @androidx.compose.runtime.Stable public boolean isEmpty();
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ property @androidx.compose.runtime.Stable public final float height;
+ property @androidx.compose.runtime.Stable public final float maxDimension;
+ property @androidx.compose.runtime.Stable public final float minDimension;
+ property @androidx.compose.runtime.Stable public final float width;
+ field public static final androidx.compose.ui.geometry.Size.Companion Companion;
+ }
+
+ public static final class Size.Companion {
+ method public long getUnspecified();
+ method public long getZero();
+ property public final long Unspecified;
+ property public final long Zero;
+ }
+
+ public final class SizeKt {
+ method @androidx.compose.runtime.Stable public static long Size(float width, float height);
+ method public static long getCenter(long);
+ method public static inline boolean isSpecified(long);
+ method public static inline boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.geometry.Size> block);
+ method @androidx.compose.runtime.Stable public static inline operator long times(double, long size);
+ method @androidx.compose.runtime.Stable public static inline operator long times(float, long size);
+ method @androidx.compose.runtime.Stable public static inline operator long times(int, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect toRect(long);
+ }
+
+}
+
diff --git a/compose/ui/ui-graphics/api/1.5.0-beta01.txt b/compose/ui/ui-graphics/api/1.5.0-beta01.txt
index eb84156..ed2ed75 100644
--- a/compose/ui/ui-graphics/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-graphics/api/1.5.0-beta01.txt
@@ -404,9 +404,6 @@
method public operator long invoke();
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGraphicsApi {
- }
-
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class FilterQuality {
method public int getValue();
property public final int value;
diff --git a/compose/ui/ui-graphics/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-graphics/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..eb84156
--- /dev/null
+++ b/compose/ui/ui-graphics/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,1632 @@
+// Signature format: 4.0
+package androidx.compose.ui.graphics {
+
+ public final class AndroidBlendMode_androidKt {
+ method public static boolean isSupported(int);
+ }
+
+ public final class AndroidCanvas_androidKt {
+ method public static androidx.compose.ui.graphics.Canvas Canvas(android.graphics.Canvas c);
+ method public static android.graphics.Canvas getNativeCanvas(androidx.compose.ui.graphics.Canvas);
+ }
+
+ public final class AndroidColorFilter_androidKt {
+ method public static android.graphics.ColorFilter asAndroidColorFilter(androidx.compose.ui.graphics.ColorFilter);
+ method public static androidx.compose.ui.graphics.ColorFilter asComposeColorFilter(android.graphics.ColorFilter);
+ }
+
+ public final class AndroidColorSpace_androidKt {
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static android.graphics.ColorSpace toAndroidColorSpace(androidx.compose.ui.graphics.colorspace.ColorSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.colorspace.ColorSpace toComposeColorSpace(android.graphics.ColorSpace);
+ }
+
+ public final class AndroidImageBitmap_androidKt {
+ method public static android.graphics.Bitmap asAndroidBitmap(androidx.compose.ui.graphics.ImageBitmap);
+ method public static androidx.compose.ui.graphics.ImageBitmap asImageBitmap(android.graphics.Bitmap);
+ }
+
+ public final class AndroidMatrixConversions_androidKt {
+ method public static void setFrom(android.graphics.Matrix, float[] matrix);
+ method public static void setFrom(float[], android.graphics.Matrix matrix);
+ }
+
+ public final class AndroidPaint implements androidx.compose.ui.graphics.Paint {
+ ctor public AndroidPaint();
+ ctor public AndroidPaint(android.graphics.Paint internalPaint);
+ method public android.graphics.Paint asFrameworkPaint();
+ method public float getAlpha();
+ method public int getBlendMode();
+ method public long getColor();
+ method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
+ method public int getFilterQuality();
+ method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
+ method public android.graphics.Shader? getShader();
+ method public int getStrokeCap();
+ method public int getStrokeJoin();
+ method public float getStrokeMiterLimit();
+ method public float getStrokeWidth();
+ method public int getStyle();
+ method public boolean isAntiAlias();
+ method public void setAlpha(float);
+ method public void setAntiAlias(boolean);
+ method public void setBlendMode(int);
+ method public void setColor(long);
+ method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter?);
+ method public void setFilterQuality(int);
+ method public void setPathEffect(androidx.compose.ui.graphics.PathEffect?);
+ method public void setShader(android.graphics.Shader?);
+ method public void setStrokeCap(int);
+ method public void setStrokeJoin(int);
+ method public void setStrokeMiterLimit(float);
+ method public void setStrokeWidth(float);
+ method public void setStyle(int);
+ property public float alpha;
+ property public int blendMode;
+ property public long color;
+ property public androidx.compose.ui.graphics.ColorFilter? colorFilter;
+ property public int filterQuality;
+ property public boolean isAntiAlias;
+ property public androidx.compose.ui.graphics.PathEffect? pathEffect;
+ property public android.graphics.Shader? shader;
+ property public int strokeCap;
+ property public int strokeJoin;
+ property public float strokeMiterLimit;
+ property public float strokeWidth;
+ property public int style;
+ }
+
+ public final class AndroidPaint_androidKt {
+ method public static androidx.compose.ui.graphics.Paint Paint();
+ method public static androidx.compose.ui.graphics.Paint toComposePaint(android.graphics.Paint);
+ }
+
+ public final class AndroidPath implements androidx.compose.ui.graphics.Path {
+ ctor public AndroidPath(optional android.graphics.Path internalPath);
+ method public void addArc(androidx.compose.ui.geometry.Rect oval, float startAngleDegrees, float sweepAngleDegrees);
+ method public void addArcRad(androidx.compose.ui.geometry.Rect oval, float startAngleRadians, float sweepAngleRadians);
+ method public void addOval(androidx.compose.ui.geometry.Rect oval);
+ method public void addPath(androidx.compose.ui.graphics.Path path, long offset);
+ method public void addRect(androidx.compose.ui.geometry.Rect rect);
+ method public void addRoundRect(androidx.compose.ui.geometry.RoundRect roundRect);
+ method public void arcTo(androidx.compose.ui.geometry.Rect rect, float startAngleDegrees, float sweepAngleDegrees, boolean forceMoveTo);
+ method public void close();
+ method public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public int getFillType();
+ method public android.graphics.Path getInternalPath();
+ method public boolean isConvex();
+ method public boolean isEmpty();
+ method public void lineTo(float x, float y);
+ method public void moveTo(float x, float y);
+ method public boolean op(androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2, int operation);
+ method public void quadraticBezierTo(float x1, float y1, float x2, float y2);
+ method public void relativeCubicTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public void relativeLineTo(float dx, float dy);
+ method public void relativeMoveTo(float dx, float dy);
+ method public void relativeQuadraticBezierTo(float dx1, float dy1, float dx2, float dy2);
+ method public void reset();
+ method public void setFillType(int);
+ method public void translate(long offset);
+ property public int fillType;
+ property public final android.graphics.Path internalPath;
+ property public boolean isConvex;
+ property public boolean isEmpty;
+ }
+
+ public final class AndroidPathEffect_androidKt {
+ method public static android.graphics.PathEffect asAndroidPathEffect(androidx.compose.ui.graphics.PathEffect);
+ method public static androidx.compose.ui.graphics.PathEffect toComposePathEffect(android.graphics.PathEffect);
+ }
+
+ public final class AndroidPathMeasure implements androidx.compose.ui.graphics.PathMeasure {
+ method public float getLength();
+ method public long getPosition(float distance);
+ method public boolean getSegment(float startDistance, float stopDistance, androidx.compose.ui.graphics.Path destination, boolean startWithMoveTo);
+ method public long getTangent(float distance);
+ method public void setPath(androidx.compose.ui.graphics.Path? path, boolean forceClosed);
+ property public float length;
+ }
+
+ public final class AndroidPathMeasure_androidKt {
+ method public static androidx.compose.ui.graphics.PathMeasure PathMeasure();
+ }
+
+ public final class AndroidPath_androidKt {
+ method public static androidx.compose.ui.graphics.Path Path();
+ method public static inline android.graphics.Path asAndroidPath(androidx.compose.ui.graphics.Path);
+ method public static androidx.compose.ui.graphics.Path asComposePath(android.graphics.Path);
+ }
+
+ public final class AndroidRenderEffect_androidKt {
+ method public static androidx.compose.ui.graphics.RenderEffect asComposeRenderEffect(android.graphics.RenderEffect);
+ }
+
+ public final class AndroidTileMode_androidKt {
+ method public static boolean isSupported(int);
+ method public static android.graphics.Shader.TileMode toAndroidTileMode(int);
+ method public static int toComposeTileMode(android.graphics.Shader.TileMode);
+ }
+
+ public final class AndroidVertexMode_androidKt {
+ method public static android.graphics.Canvas.VertexMode toAndroidVertexMode(int);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class BlendMode {
+ field public static final androidx.compose.ui.graphics.BlendMode.Companion Companion;
+ }
+
+ public static final class BlendMode.Companion {
+ method public int getClear();
+ method public int getColor();
+ method public int getColorBurn();
+ method public int getColorDodge();
+ method public int getDarken();
+ method public int getDifference();
+ method public int getDst();
+ method public int getDstAtop();
+ method public int getDstIn();
+ method public int getDstOut();
+ method public int getDstOver();
+ method public int getExclusion();
+ method public int getHardlight();
+ method public int getHue();
+ method public int getLighten();
+ method public int getLuminosity();
+ method public int getModulate();
+ method public int getMultiply();
+ method public int getOverlay();
+ method public int getPlus();
+ method public int getSaturation();
+ method public int getScreen();
+ method public int getSoftlight();
+ method public int getSrc();
+ method public int getSrcAtop();
+ method public int getSrcIn();
+ method public int getSrcOut();
+ method public int getSrcOver();
+ method public int getXor();
+ property public final int Clear;
+ property public final int Color;
+ property public final int ColorBurn;
+ property public final int ColorDodge;
+ property public final int Darken;
+ property public final int Difference;
+ property public final int Dst;
+ property public final int DstAtop;
+ property public final int DstIn;
+ property public final int DstOut;
+ property public final int DstOver;
+ property public final int Exclusion;
+ property public final int Hardlight;
+ property public final int Hue;
+ property public final int Lighten;
+ property public final int Luminosity;
+ property public final int Modulate;
+ property public final int Multiply;
+ property public final int Overlay;
+ property public final int Plus;
+ property public final int Saturation;
+ property public final int Screen;
+ property public final int Softlight;
+ property public final int Src;
+ property public final int SrcAtop;
+ property public final int SrcIn;
+ property public final int SrcOut;
+ property public final int SrcOver;
+ property public final int Xor;
+ }
+
+ @androidx.compose.runtime.Immutable public final class BlurEffect extends androidx.compose.ui.graphics.RenderEffect {
+ ctor public BlurEffect(androidx.compose.ui.graphics.RenderEffect? renderEffect, float radiusX, float radiusY, int edgeTreatment);
+ method @RequiresApi(android.os.Build.VERSION_CODES.S) protected android.graphics.RenderEffect createRenderEffect();
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class Brush {
+ method public abstract void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+ method public long getIntrinsicSize();
+ property public long intrinsicSize;
+ field public static final androidx.compose.ui.graphics.Brush.Companion Companion;
+ }
+
+ public static final class Brush.Companion {
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startX, optional float endX, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush horizontalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startX, optional float endX, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long start, optional long end, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush linearGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long start, optional long end, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center, optional float radius, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush radialGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center, optional float radius, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional long center);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush sweepGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional long center);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(java.util.List<androidx.compose.ui.graphics.Color> colors, optional float startY, optional float endY, optional int tileMode);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.Brush verticalGradient(kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional float startY, optional float endY, optional int tileMode);
+ }
+
+ public final class BrushKt {
+ method public static androidx.compose.ui.graphics.ShaderBrush ShaderBrush(android.graphics.Shader shader);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface Canvas {
+ method public void clipPath(androidx.compose.ui.graphics.Path path, optional int clipOp);
+ method public default void clipRect(androidx.compose.ui.geometry.Rect rect, optional int clipOp);
+ method public void clipRect(float left, float top, float right, float bottom, optional int clipOp);
+ method public void concat(float[] matrix);
+ method public void disableZ();
+ method public default void drawArc(androidx.compose.ui.geometry.Rect rect, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
+ method public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
+ method public default void drawArcRad(androidx.compose.ui.geometry.Rect rect, float startAngleRad, float sweepAngleRad, boolean useCenter, androidx.compose.ui.graphics.Paint paint);
+ method public void drawCircle(long center, float radius, androidx.compose.ui.graphics.Paint paint);
+ method public void drawImage(androidx.compose.ui.graphics.ImageBitmap image, long topLeftOffset, androidx.compose.ui.graphics.Paint paint);
+ method public void drawImageRect(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, androidx.compose.ui.graphics.Paint paint);
+ method public void drawLine(long p1, long p2, androidx.compose.ui.graphics.Paint paint);
+ method public default void drawOval(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.graphics.Paint paint);
+ method public void drawOval(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
+ method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Paint paint);
+ method public void drawPoints(int pointMode, java.util.List<androidx.compose.ui.geometry.Offset> points, androidx.compose.ui.graphics.Paint paint);
+ method public void drawRawPoints(int pointMode, float[] points, androidx.compose.ui.graphics.Paint paint);
+ method public default void drawRect(androidx.compose.ui.geometry.Rect rect, androidx.compose.ui.graphics.Paint paint);
+ method public void drawRect(float left, float top, float right, float bottom, androidx.compose.ui.graphics.Paint paint);
+ method public void drawRoundRect(float left, float top, float right, float bottom, float radiusX, float radiusY, androidx.compose.ui.graphics.Paint paint);
+ method public void drawVertices(androidx.compose.ui.graphics.Vertices vertices, int blendMode, androidx.compose.ui.graphics.Paint paint);
+ method public void enableZ();
+ method public void restore();
+ method public void rotate(float degrees);
+ method public void save();
+ method public void saveLayer(androidx.compose.ui.geometry.Rect bounds, androidx.compose.ui.graphics.Paint paint);
+ method public void scale(float sx, optional float sy);
+ method public void skew(float sx, float sy);
+ method public default void skewRad(float sxRad, float syRad);
+ method public void translate(float dx, float dy);
+ }
+
+ public final class CanvasHolder {
+ ctor public CanvasHolder();
+ method public inline void drawInto(android.graphics.Canvas targetCanvas, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Canvas,kotlin.Unit> block);
+ }
+
+ public final class CanvasKt {
+ method public static androidx.compose.ui.graphics.Canvas Canvas(androidx.compose.ui.graphics.ImageBitmap image);
+ method public static void rotate(androidx.compose.ui.graphics.Canvas, float degrees, float pivotX, float pivotY);
+ method public static void rotateRad(androidx.compose.ui.graphics.Canvas, float radians, optional float pivotX, optional float pivotY);
+ method public static void scale(androidx.compose.ui.graphics.Canvas, float sx, optional float sy, float pivotX, float pivotY);
+ method public static inline void withSave(androidx.compose.ui.graphics.Canvas, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ method public static inline void withSaveLayer(androidx.compose.ui.graphics.Canvas, androidx.compose.ui.geometry.Rect bounds, androidx.compose.ui.graphics.Paint paint, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class ClipOp {
+ field public static final androidx.compose.ui.graphics.ClipOp.Companion Companion;
+ }
+
+ public static final class ClipOp.Companion {
+ method public int getDifference();
+ method public int getIntersect();
+ property public final int Difference;
+ property public final int Intersect;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Color {
+ ctor public Color(long value);
+ method @androidx.compose.runtime.Stable public operator float component1();
+ method @androidx.compose.runtime.Stable public operator float component2();
+ method @androidx.compose.runtime.Stable public operator float component3();
+ method @androidx.compose.runtime.Stable public operator float component4();
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.graphics.colorspace.ColorSpace component5();
+ method public long convert(androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+ method @androidx.compose.runtime.Stable public long copy(optional float alpha, optional float red, optional float green, optional float blue);
+ method public float getAlpha();
+ method public float getBlue();
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace getColorSpace();
+ method public float getGreen();
+ method public float getRed();
+ method public long getValue();
+ property @androidx.compose.runtime.Stable public final float alpha;
+ property @androidx.compose.runtime.Stable public final float blue;
+ property @androidx.compose.runtime.Stable public final androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace;
+ property @androidx.compose.runtime.Stable public final float green;
+ property @androidx.compose.runtime.Stable public final float red;
+ property public final long value;
+ field public static final androidx.compose.ui.graphics.Color.Companion Companion;
+ }
+
+ public static final class Color.Companion {
+ method public long getBlack();
+ method public long getBlue();
+ method public long getCyan();
+ method public long getDarkGray();
+ method public long getGray();
+ method public long getGreen();
+ method public long getLightGray();
+ method public long getMagenta();
+ method public long getRed();
+ method public long getTransparent();
+ method public long getUnspecified();
+ method public long getWhite();
+ method public long getYellow();
+ method public long hsl(float hue, float saturation, float lightness, optional float alpha, optional androidx.compose.ui.graphics.colorspace.Rgb colorSpace);
+ method public long hsv(float hue, float saturation, float value, optional float alpha, optional androidx.compose.ui.graphics.colorspace.Rgb colorSpace);
+ property public final long Black;
+ property public final long Blue;
+ property public final long Cyan;
+ property public final long DarkGray;
+ property public final long Gray;
+ property public final long Green;
+ property public final long LightGray;
+ property public final long Magenta;
+ property public final long Red;
+ property public final long Transparent;
+ property public final long Unspecified;
+ property public final long White;
+ property public final long Yellow;
+ }
+
+ @androidx.compose.runtime.Immutable public final class ColorFilter {
+ field public static final androidx.compose.ui.graphics.ColorFilter.Companion Companion;
+ }
+
+ public static final class ColorFilter.Companion {
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.ColorFilter colorMatrix(float[] colorMatrix);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.ColorFilter lighting(long multiply, long add);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.graphics.ColorFilter tint(long color, optional int blendMode);
+ }
+
+ public final class ColorKt {
+ method @androidx.compose.runtime.Stable public static long Color(float red, float green, float blue, optional float alpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+ method @androidx.compose.runtime.Stable public static long Color(int color);
+ method @androidx.compose.runtime.Stable public static long Color(int red, int green, int blue, optional int alpha);
+ method @androidx.compose.runtime.Stable public static long Color(long color);
+ method @androidx.compose.runtime.Stable public static long compositeOver(long, long background);
+ method public static inline boolean isSpecified(long);
+ method public static inline boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static float luminance(long);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.graphics.Color> block);
+ method @androidx.compose.runtime.Stable public static int toArgb(long);
+ }
+
+ @kotlin.jvm.JvmInline public final value class ColorMatrix {
+ ctor public ColorMatrix(optional float[] values);
+ method public void convertRgbToYuv();
+ method public void convertYuvToRgb();
+ method public inline operator float get(int row, int column);
+ method public float[] getValues();
+ method public void reset();
+ method public void set(float[] src);
+ method public inline operator void set(int row, int column, float v);
+ method public void setToRotateBlue(float degrees);
+ method public void setToRotateGreen(float degrees);
+ method public void setToRotateRed(float degrees);
+ method public void setToSaturation(float sat);
+ method public void setToScale(float redScale, float greenScale, float blueScale, float alphaScale);
+ method public operator void timesAssign(float[] colorMatrix);
+ property public final float[] values;
+ }
+
+ public fun interface ColorProducer {
+ method public operator long invoke();
+ }
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGraphicsApi {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class FilterQuality {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.ui.graphics.FilterQuality.Companion Companion;
+ }
+
+ public static final class FilterQuality.Companion {
+ method public int getHigh();
+ method public int getLow();
+ method public int getMedium();
+ method public int getNone();
+ property public final int High;
+ property public final int Low;
+ property public final int Medium;
+ property public final int None;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ImageBitmap {
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace getColorSpace();
+ method public int getConfig();
+ method public boolean getHasAlpha();
+ method public int getHeight();
+ method public int getWidth();
+ method public void prepareToDraw();
+ method public void readPixels(int[] buffer, optional int startX, optional int startY, optional int width, optional int height, optional int bufferOffset, optional int stride);
+ property public abstract androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace;
+ property public abstract int config;
+ property public abstract boolean hasAlpha;
+ property public abstract int height;
+ property public abstract int width;
+ field public static final androidx.compose.ui.graphics.ImageBitmap.Companion Companion;
+ }
+
+ public static final class ImageBitmap.Companion {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class ImageBitmapConfig {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.ui.graphics.ImageBitmapConfig.Companion Companion;
+ }
+
+ public static final class ImageBitmapConfig.Companion {
+ method public int getAlpha8();
+ method public int getArgb8888();
+ method public int getF16();
+ method public int getGpu();
+ method public int getRgb565();
+ property public final int Alpha8;
+ property public final int Argb8888;
+ property public final int F16;
+ property public final int Gpu;
+ property public final int Rgb565;
+ }
+
+ public final class ImageBitmapKt {
+ method public static androidx.compose.ui.graphics.ImageBitmap ImageBitmap(int width, int height, optional int config, optional boolean hasAlpha, optional androidx.compose.ui.graphics.colorspace.ColorSpace colorSpace);
+ method public static androidx.compose.ui.graphics.PixelMap toPixelMap(androidx.compose.ui.graphics.ImageBitmap, optional int startX, optional int startY, optional int width, optional int height, optional int[] buffer, optional int bufferOffset, optional int stride);
+ }
+
+ @androidx.compose.runtime.Immutable public final class LinearGradient extends androidx.compose.ui.graphics.ShaderBrush {
+ method public android.graphics.Shader createShader(long size);
+ property public long intrinsicSize;
+ }
+
+ @kotlin.jvm.JvmInline public final value class Matrix {
+ ctor public Matrix(optional float[] values);
+ method public inline operator float get(int row, int column);
+ method public float[] getValues();
+ method public void invert();
+ method public void map(androidx.compose.ui.geometry.MutableRect rect);
+ method public androidx.compose.ui.geometry.Rect map(androidx.compose.ui.geometry.Rect rect);
+ method public long map(long point);
+ method public void reset();
+ method public void rotateX(float degrees);
+ method public void rotateY(float degrees);
+ method public void rotateZ(float degrees);
+ method public void scale(optional float x, optional float y, optional float z);
+ method public inline operator void set(int row, int column, float v);
+ method public void setFrom(float[] matrix);
+ method public operator void timesAssign(float[] m);
+ method public void translate(optional float x, optional float y, optional float z);
+ property public final float[] values;
+ field public static final androidx.compose.ui.graphics.Matrix.Companion Companion;
+ field public static final int Perspective0 = 3; // 0x3
+ field public static final int Perspective1 = 7; // 0x7
+ field public static final int Perspective2 = 15; // 0xf
+ field public static final int ScaleX = 0; // 0x0
+ field public static final int ScaleY = 5; // 0x5
+ field public static final int ScaleZ = 10; // 0xa
+ field public static final int SkewX = 4; // 0x4
+ field public static final int SkewY = 1; // 0x1
+ field public static final int TranslateX = 12; // 0xc
+ field public static final int TranslateY = 13; // 0xd
+ field public static final int TranslateZ = 14; // 0xe
+ }
+
+ public static final class Matrix.Companion {
+ }
+
+ public final class MatrixKt {
+ method public static boolean isIdentity(float[]);
+ }
+
+ @androidx.compose.runtime.Immutable public final class OffsetEffect extends androidx.compose.ui.graphics.RenderEffect {
+ ctor public OffsetEffect(androidx.compose.ui.graphics.RenderEffect? renderEffect, long offset);
+ method @RequiresApi(android.os.Build.VERSION_CODES.S) protected android.graphics.RenderEffect createRenderEffect();
+ }
+
+ public abstract sealed class Outline {
+ method public abstract androidx.compose.ui.geometry.Rect getBounds();
+ property public abstract androidx.compose.ui.geometry.Rect bounds;
+ }
+
+ public static final class Outline.Generic extends androidx.compose.ui.graphics.Outline {
+ ctor public Outline.Generic(androidx.compose.ui.graphics.Path path);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public androidx.compose.ui.graphics.Path getPath();
+ property public androidx.compose.ui.geometry.Rect bounds;
+ property public final androidx.compose.ui.graphics.Path path;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class Outline.Rectangle extends androidx.compose.ui.graphics.Outline {
+ ctor public Outline.Rectangle(androidx.compose.ui.geometry.Rect rect);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public androidx.compose.ui.geometry.Rect getRect();
+ property public androidx.compose.ui.geometry.Rect bounds;
+ property public final androidx.compose.ui.geometry.Rect rect;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class Outline.Rounded extends androidx.compose.ui.graphics.Outline {
+ ctor public Outline.Rounded(androidx.compose.ui.geometry.RoundRect roundRect);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public androidx.compose.ui.geometry.RoundRect getRoundRect();
+ property public androidx.compose.ui.geometry.Rect bounds;
+ property public final androidx.compose.ui.geometry.RoundRect roundRect;
+ }
+
+ public final class OutlineKt {
+ method public static void addOutline(androidx.compose.ui.graphics.Path, androidx.compose.ui.graphics.Outline outline);
+ method public static void drawOutline(androidx.compose.ui.graphics.Canvas, androidx.compose.ui.graphics.Outline outline, androidx.compose.ui.graphics.Paint paint);
+ method public static void drawOutline(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.graphics.Outline outline, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public static void drawOutline(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.graphics.Outline outline, long color, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ }
+
+ public interface Paint {
+ method public android.graphics.Paint asFrameworkPaint();
+ method public float getAlpha();
+ method public int getBlendMode();
+ method public long getColor();
+ method public androidx.compose.ui.graphics.ColorFilter? getColorFilter();
+ method public int getFilterQuality();
+ method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
+ method public android.graphics.Shader? getShader();
+ method public int getStrokeCap();
+ method public int getStrokeJoin();
+ method public float getStrokeMiterLimit();
+ method public float getStrokeWidth();
+ method public int getStyle();
+ method public boolean isAntiAlias();
+ method public void setAlpha(float);
+ method public void setAntiAlias(boolean);
+ method public void setBlendMode(int);
+ method public void setColor(long);
+ method public void setColorFilter(androidx.compose.ui.graphics.ColorFilter?);
+ method public void setFilterQuality(int);
+ method public void setPathEffect(androidx.compose.ui.graphics.PathEffect?);
+ method public void setShader(android.graphics.Shader?);
+ method public void setStrokeCap(int);
+ method public void setStrokeJoin(int);
+ method public void setStrokeMiterLimit(float);
+ method public void setStrokeWidth(float);
+ method public void setStyle(int);
+ property public abstract float alpha;
+ property public abstract int blendMode;
+ property public abstract long color;
+ property public abstract androidx.compose.ui.graphics.ColorFilter? colorFilter;
+ property public abstract int filterQuality;
+ property public abstract boolean isAntiAlias;
+ property public abstract androidx.compose.ui.graphics.PathEffect? pathEffect;
+ property public abstract android.graphics.Shader? shader;
+ property public abstract int strokeCap;
+ property public abstract int strokeJoin;
+ property public abstract float strokeMiterLimit;
+ property public abstract float strokeWidth;
+ property public abstract int style;
+ }
+
+ public final class PaintKt {
+ method public static androidx.compose.ui.graphics.Paint Paint();
+ field public static final float DefaultAlpha = 1.0f;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class PaintingStyle {
+ field public static final androidx.compose.ui.graphics.PaintingStyle.Companion Companion;
+ }
+
+ public static final class PaintingStyle.Companion {
+ method public int getFill();
+ method public int getStroke();
+ property public final int Fill;
+ property public final int Stroke;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface Path {
+ method public void addArc(androidx.compose.ui.geometry.Rect oval, float startAngleDegrees, float sweepAngleDegrees);
+ method public void addArcRad(androidx.compose.ui.geometry.Rect oval, float startAngleRadians, float sweepAngleRadians);
+ method public void addOval(androidx.compose.ui.geometry.Rect oval);
+ method public void addPath(androidx.compose.ui.graphics.Path path, optional long offset);
+ method public void addRect(androidx.compose.ui.geometry.Rect rect);
+ method public void addRoundRect(androidx.compose.ui.geometry.RoundRect roundRect);
+ method public void arcTo(androidx.compose.ui.geometry.Rect rect, float startAngleDegrees, float sweepAngleDegrees, boolean forceMoveTo);
+ method public default void arcToRad(androidx.compose.ui.geometry.Rect rect, float startAngleRadians, float sweepAngleRadians, boolean forceMoveTo);
+ method public void close();
+ method public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public int getFillType();
+ method public boolean isConvex();
+ method public boolean isEmpty();
+ method public void lineTo(float x, float y);
+ method public void moveTo(float x, float y);
+ method public boolean op(androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2, int operation);
+ method public void quadraticBezierTo(float x1, float y1, float x2, float y2);
+ method public void relativeCubicTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public void relativeLineTo(float dx, float dy);
+ method public void relativeMoveTo(float dx, float dy);
+ method public void relativeQuadraticBezierTo(float dx1, float dy1, float dx2, float dy2);
+ method public void reset();
+ method public default void rewind();
+ method public void setFillType(int);
+ method public default void transform(float[] matrix);
+ method public void translate(long offset);
+ property public abstract int fillType;
+ property public abstract boolean isConvex;
+ property public abstract boolean isEmpty;
+ field public static final androidx.compose.ui.graphics.Path.Companion Companion;
+ }
+
+ public static final class Path.Companion {
+ method public androidx.compose.ui.graphics.Path combine(int operation, androidx.compose.ui.graphics.Path path1, androidx.compose.ui.graphics.Path path2);
+ }
+
+ public interface PathEffect {
+ field public static final androidx.compose.ui.graphics.PathEffect.Companion Companion;
+ }
+
+ public static final class PathEffect.Companion {
+ method public androidx.compose.ui.graphics.PathEffect chainPathEffect(androidx.compose.ui.graphics.PathEffect outer, androidx.compose.ui.graphics.PathEffect inner);
+ method public androidx.compose.ui.graphics.PathEffect cornerPathEffect(float radius);
+ method public androidx.compose.ui.graphics.PathEffect dashPathEffect(float[] intervals, optional float phase);
+ method public androidx.compose.ui.graphics.PathEffect stampedPathEffect(androidx.compose.ui.graphics.Path shape, float advance, float phase, int style);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class PathFillType {
+ field public static final androidx.compose.ui.graphics.PathFillType.Companion Companion;
+ }
+
+ public static final class PathFillType.Companion {
+ method public int getEvenOdd();
+ method public int getNonZero();
+ property public final int EvenOdd;
+ property public final int NonZero;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface PathMeasure {
+ method public float getLength();
+ method public long getPosition(float distance);
+ method public boolean getSegment(float startDistance, float stopDistance, androidx.compose.ui.graphics.Path destination, optional boolean startWithMoveTo);
+ method public long getTangent(float distance);
+ method public void setPath(androidx.compose.ui.graphics.Path? path, boolean forceClosed);
+ property public abstract float length;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class PathOperation {
+ field public static final androidx.compose.ui.graphics.PathOperation.Companion Companion;
+ }
+
+ public static final class PathOperation.Companion {
+ method public int getDifference();
+ method public int getIntersect();
+ method public int getReverseDifference();
+ method public int getUnion();
+ method public int getXor();
+ property public final int Difference;
+ property public final int Intersect;
+ property public final int ReverseDifference;
+ property public final int Union;
+ property public final int Xor;
+ }
+
+ public final class PathOperationKt {
+ method @Deprecated public static int getDifference(androidx.compose.ui.graphics.PathOperation.Companion);
+ method @Deprecated public static int getIntersect(androidx.compose.ui.graphics.PathOperation.Companion);
+ method @Deprecated public static int getReverseDifference(androidx.compose.ui.graphics.PathOperation.Companion);
+ method @Deprecated public static int getUnion(androidx.compose.ui.graphics.PathOperation.Companion);
+ method @Deprecated public static int getXor(androidx.compose.ui.graphics.PathOperation.Companion);
+ }
+
+ public final class PixelMap {
+ ctor public PixelMap(int[] buffer, int width, int height, int bufferOffset, int stride);
+ method public operator long get(int x, int y);
+ method public int[] getBuffer();
+ method public int getBufferOffset();
+ method public int getHeight();
+ method public int getStride();
+ method public int getWidth();
+ property public final int[] buffer;
+ property public final int bufferOffset;
+ property public final int height;
+ property public final int stride;
+ property public final int width;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class PointMode {
+ field public static final androidx.compose.ui.graphics.PointMode.Companion Companion;
+ }
+
+ public static final class PointMode.Companion {
+ method public int getLines();
+ method public int getPoints();
+ method public int getPolygon();
+ property public final int Lines;
+ property public final int Points;
+ property public final int Polygon;
+ }
+
+ @androidx.compose.runtime.Immutable public final class RadialGradient extends androidx.compose.ui.graphics.ShaderBrush {
+ method public android.graphics.Shader createShader(long size);
+ property public long intrinsicSize;
+ }
+
+ public final class RectHelper_androidKt {
+ method @Deprecated public static android.graphics.Rect toAndroidRect(androidx.compose.ui.geometry.Rect);
+ method public static android.graphics.Rect toAndroidRect(androidx.compose.ui.unit.IntRect);
+ method public static android.graphics.RectF toAndroidRectF(androidx.compose.ui.geometry.Rect);
+ method public static androidx.compose.ui.unit.IntRect toComposeIntRect(android.graphics.Rect);
+ method public static androidx.compose.ui.geometry.Rect toComposeRect(android.graphics.Rect);
+ }
+
+ public final class RectangleShapeKt {
+ method public static androidx.compose.ui.graphics.Shape getRectangleShape();
+ property public static final androidx.compose.ui.graphics.Shape RectangleShape;
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class RenderEffect {
+ method @RequiresApi(android.os.Build.VERSION_CODES.S) public final android.graphics.RenderEffect asAndroidRenderEffect();
+ method @RequiresApi(android.os.Build.VERSION_CODES.S) protected abstract android.graphics.RenderEffect createRenderEffect();
+ method public boolean isSupported();
+ }
+
+ public final class RenderEffectKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.BlurEffect BlurEffect(float radiusX, float radiusY, optional int edgeTreatment);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.OffsetEffect OffsetEffect(float offsetX, float offsetY);
+ }
+
+ @androidx.compose.runtime.Immutable public abstract class ShaderBrush extends androidx.compose.ui.graphics.Brush {
+ ctor public ShaderBrush();
+ method public final void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+ method public abstract android.graphics.Shader createShader(long size);
+ }
+
+ public final class ShaderKt {
+ method public static android.graphics.Shader ImageShader(androidx.compose.ui.graphics.ImageBitmap image, optional int tileModeX, optional int tileModeY);
+ method public static android.graphics.Shader LinearGradientShader(long from, long to, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional int tileMode);
+ method public static android.graphics.Shader RadialGradientShader(long center, float radius, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops, optional int tileMode);
+ method public static android.graphics.Shader SweepGradientShader(long center, java.util.List<androidx.compose.ui.graphics.Color> colors, optional java.util.List<java.lang.Float>? colorStops);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Shadow {
+ ctor public Shadow(optional @androidx.compose.runtime.Stable long color, optional @androidx.compose.runtime.Stable long offset, optional @androidx.compose.runtime.Stable float blurRadius);
+ method public androidx.compose.ui.graphics.Shadow copy(optional long color, optional long offset, optional float blurRadius);
+ method public float getBlurRadius();
+ method public long getColor();
+ method public long getOffset();
+ property public final float blurRadius;
+ property public final long color;
+ property public final long offset;
+ field public static final androidx.compose.ui.graphics.Shadow.Companion Companion;
+ }
+
+ public static final class Shadow.Companion {
+ method public androidx.compose.ui.graphics.Shadow getNone();
+ property public final androidx.compose.ui.graphics.Shadow None;
+ }
+
+ public final class ShadowKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.Shadow lerp(androidx.compose.ui.graphics.Shadow start, androidx.compose.ui.graphics.Shadow stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public interface Shape {
+ method public androidx.compose.ui.graphics.Outline createOutline(long size, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.unit.Density density);
+ }
+
+ @androidx.compose.runtime.Immutable public final class SolidColor extends androidx.compose.ui.graphics.Brush {
+ ctor public SolidColor(long value);
+ method public void applyTo(long size, androidx.compose.ui.graphics.Paint p, float alpha);
+ method public long getValue();
+ property public final long value;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class StampedPathEffectStyle {
+ field public static final androidx.compose.ui.graphics.StampedPathEffectStyle.Companion Companion;
+ }
+
+ public static final class StampedPathEffectStyle.Companion {
+ method public int getMorph();
+ method public int getRotate();
+ method public int getTranslate();
+ property public final int Morph;
+ property public final int Rotate;
+ property public final int Translate;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class StrokeCap {
+ field public static final androidx.compose.ui.graphics.StrokeCap.Companion Companion;
+ }
+
+ public static final class StrokeCap.Companion {
+ method public int getButt();
+ method public int getRound();
+ method public int getSquare();
+ property public final int Butt;
+ property public final int Round;
+ property public final int Square;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class StrokeJoin {
+ field public static final androidx.compose.ui.graphics.StrokeJoin.Companion Companion;
+ }
+
+ public static final class StrokeJoin.Companion {
+ method public int getBevel();
+ method public int getMiter();
+ method public int getRound();
+ property public final int Bevel;
+ property public final int Miter;
+ property public final int Round;
+ }
+
+ @androidx.compose.runtime.Immutable public final class SweepGradient extends androidx.compose.ui.graphics.ShaderBrush {
+ method public android.graphics.Shader createShader(long size);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TileMode {
+ field public static final androidx.compose.ui.graphics.TileMode.Companion Companion;
+ }
+
+ public static final class TileMode.Companion {
+ method public int getClamp();
+ method public int getDecal();
+ method public int getMirror();
+ method public int getRepeated();
+ property public final int Clamp;
+ property public final int Decal;
+ property public final int Mirror;
+ property public final int Repeated;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class VertexMode {
+ field public static final androidx.compose.ui.graphics.VertexMode.Companion Companion;
+ }
+
+ public static final class VertexMode.Companion {
+ method public int getTriangleFan();
+ method public int getTriangleStrip();
+ method public int getTriangles();
+ property public final int TriangleFan;
+ property public final int TriangleStrip;
+ property public final int Triangles;
+ }
+
+ public final class Vertices {
+ ctor public Vertices(int vertexMode, java.util.List<androidx.compose.ui.geometry.Offset> positions, java.util.List<androidx.compose.ui.geometry.Offset> textureCoordinates, java.util.List<androidx.compose.ui.graphics.Color> colors, java.util.List<java.lang.Integer> indices);
+ method public int[] getColors();
+ method public short[] getIndices();
+ method public float[] getPositions();
+ method public float[] getTextureCoordinates();
+ method public int getVertexMode();
+ property public final int[] colors;
+ property public final short[] indices;
+ property public final float[] positions;
+ property public final float[] textureCoordinates;
+ property public final int vertexMode;
+ }
+
+}
+
+package androidx.compose.ui.graphics.colorspace {
+
+ public abstract class Adaptation {
+ field public static final androidx.compose.ui.graphics.colorspace.Adaptation.Companion Companion;
+ }
+
+ public static final class Adaptation.Companion {
+ method public androidx.compose.ui.graphics.colorspace.Adaptation getBradford();
+ method public androidx.compose.ui.graphics.colorspace.Adaptation getCiecat02();
+ method public androidx.compose.ui.graphics.colorspace.Adaptation getVonKries();
+ property public final androidx.compose.ui.graphics.colorspace.Adaptation Bradford;
+ property public final androidx.compose.ui.graphics.colorspace.Adaptation Ciecat02;
+ property public final androidx.compose.ui.graphics.colorspace.Adaptation VonKries;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class ColorModel {
+ method public int getComponentCount();
+ property @androidx.compose.runtime.Stable public final int componentCount;
+ field public static final androidx.compose.ui.graphics.colorspace.ColorModel.Companion Companion;
+ }
+
+ public static final class ColorModel.Companion {
+ method public long getCmyk();
+ method public long getLab();
+ method public long getRgb();
+ method public long getXyz();
+ property public final long Cmyk;
+ property public final long Lab;
+ property public final long Rgb;
+ property public final long Xyz;
+ }
+
+ public abstract class ColorSpace {
+ ctor public ColorSpace(String name, long model);
+ method public final float[] fromXyz(float x, float y, float z);
+ method public abstract float[] fromXyz(float[] v);
+ method public final int getComponentCount();
+ method public abstract float getMaxValue(int component);
+ method public abstract float getMinValue(int component);
+ method public final long getModel();
+ method public final String getName();
+ method public boolean isSrgb();
+ method public abstract boolean isWideGamut();
+ method public final float[] toXyz(float r, float g, float b);
+ method public abstract float[] toXyz(float[] v);
+ property public final int componentCount;
+ property public boolean isSrgb;
+ property public abstract boolean isWideGamut;
+ property public final long model;
+ property public final String name;
+ }
+
+ public final class ColorSpaceKt {
+ method public static androidx.compose.ui.graphics.colorspace.ColorSpace adapt(androidx.compose.ui.graphics.colorspace.ColorSpace, androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint);
+ method public static androidx.compose.ui.graphics.colorspace.ColorSpace adapt(androidx.compose.ui.graphics.colorspace.ColorSpace, androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint, optional androidx.compose.ui.graphics.colorspace.Adaptation adaptation);
+ method public static androidx.compose.ui.graphics.colorspace.Connector connect(androidx.compose.ui.graphics.colorspace.ColorSpace, optional androidx.compose.ui.graphics.colorspace.ColorSpace destination, optional int intent);
+ }
+
+ public final class ColorSpaces {
+ method public androidx.compose.ui.graphics.colorspace.Rgb getAces();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getAcescg();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getAdobeRgb();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getBt2020();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getBt709();
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace getCieLab();
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace getCieXyz();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getDciP3();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getDisplayP3();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getExtendedSrgb();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getLinearExtendedSrgb();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getLinearSrgb();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getNtsc1953();
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace getOklab();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getProPhotoRgb();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getSmpteC();
+ method public androidx.compose.ui.graphics.colorspace.Rgb getSrgb();
+ method public androidx.compose.ui.graphics.colorspace.ColorSpace? match(float[] toXYZD50, androidx.compose.ui.graphics.colorspace.TransferParameters function);
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Aces;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Acescg;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb AdobeRgb;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Bt2020;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Bt709;
+ property public final androidx.compose.ui.graphics.colorspace.ColorSpace CieLab;
+ property public final androidx.compose.ui.graphics.colorspace.ColorSpace CieXyz;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb DciP3;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb DisplayP3;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb ExtendedSrgb;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb LinearExtendedSrgb;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb LinearSrgb;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Ntsc1953;
+ property public final androidx.compose.ui.graphics.colorspace.ColorSpace Oklab;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb ProPhotoRgb;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb SmpteC;
+ property public final androidx.compose.ui.graphics.colorspace.Rgb Srgb;
+ field public static final androidx.compose.ui.graphics.colorspace.ColorSpaces INSTANCE;
+ }
+
+ public class Connector {
+ method public final androidx.compose.ui.graphics.colorspace.ColorSpace getDestination();
+ method public final int getRenderIntent();
+ method public final androidx.compose.ui.graphics.colorspace.ColorSpace getSource();
+ method public final float[] transform(float r, float g, float b);
+ method public float[] transform(float[] v);
+ property public final androidx.compose.ui.graphics.colorspace.ColorSpace destination;
+ property public final int renderIntent;
+ property public final androidx.compose.ui.graphics.colorspace.ColorSpace source;
+ }
+
+ public final class Illuminant {
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getA();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getB();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getC();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getD50();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getD55();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getD60();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getD65();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getD75();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getE();
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint A;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint B;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint C;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint D50;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint D55;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint D60;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint D65;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint D75;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint E;
+ field public static final androidx.compose.ui.graphics.colorspace.Illuminant INSTANCE;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class RenderIntent {
+ field public static final androidx.compose.ui.graphics.colorspace.RenderIntent.Companion Companion;
+ }
+
+ public static final class RenderIntent.Companion {
+ method public int getAbsolute();
+ method public int getPerceptual();
+ method public int getRelative();
+ method public int getSaturation();
+ property public final int Absolute;
+ property public final int Perceptual;
+ property public final int Relative;
+ property public final int Saturation;
+ }
+
+ public final class Rgb extends androidx.compose.ui.graphics.colorspace.ColorSpace {
+ ctor public Rgb(String name, float[] toXYZ, androidx.compose.ui.graphics.colorspace.TransferParameters function);
+ ctor public Rgb(String name, float[] primaries, androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint, androidx.compose.ui.graphics.colorspace.TransferParameters function);
+ ctor public Rgb(String name, float[] primaries, androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint, double gamma);
+ ctor public Rgb(String name, float[] primaries, androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint, kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Double> oetf, kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Double> eotf, float min, float max);
+ ctor public Rgb(String name, float[] toXYZ, double gamma);
+ ctor public Rgb(String name, float[] toXYZ, kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Double> oetf, kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Double> eotf);
+ method public float[] fromLinear(float r, float g, float b);
+ method public float[] fromLinear(float[] v);
+ method public float[] fromXyz(float[] v);
+ method public kotlin.jvm.functions.Function1<java.lang.Double,java.lang.Double> getEotf();
+ method public float[] getInverseTransform();
+ method public float[] getInverseTransform(float[] inverseTransform);
+ method public float getMaxValue(int component);
+ method public float getMinValue(int component);
+ method public kotlin.jvm.functions.Function1<java.lang.Double,java.lang.Double> getOetf();
+ method public float[] getPrimaries();
+ method public float[] getPrimaries(float[] primaries);
+ method public androidx.compose.ui.graphics.colorspace.TransferParameters? getTransferParameters();
+ method public float[] getTransform();
+ method public float[] getTransform(float[] transform);
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint getWhitePoint();
+ method public boolean isWideGamut();
+ method public float[] toLinear(float r, float g, float b);
+ method public float[] toLinear(float[] v);
+ method public float[] toXyz(float[] v);
+ property public final kotlin.jvm.functions.Function1<java.lang.Double,java.lang.Double> eotf;
+ property public boolean isSrgb;
+ property public boolean isWideGamut;
+ property public final kotlin.jvm.functions.Function1<java.lang.Double,java.lang.Double> oetf;
+ property public final androidx.compose.ui.graphics.colorspace.TransferParameters? transferParameters;
+ property public final androidx.compose.ui.graphics.colorspace.WhitePoint whitePoint;
+ }
+
+ public final class TransferParameters {
+ ctor public TransferParameters(double gamma, double a, double b, double c, double d, optional double e, optional double f);
+ method public double component1();
+ method public double component2();
+ method public double component3();
+ method public double component4();
+ method public double component5();
+ method public double component6();
+ method public double component7();
+ method public androidx.compose.ui.graphics.colorspace.TransferParameters copy(double gamma, double a, double b, double c, double d, double e, double f);
+ method public double getA();
+ method public double getB();
+ method public double getC();
+ method public double getD();
+ method public double getE();
+ method public double getF();
+ method public double getGamma();
+ property public final double a;
+ property public final double b;
+ property public final double c;
+ property public final double d;
+ property public final double e;
+ property public final double f;
+ property public final double gamma;
+ }
+
+ public final class WhitePoint {
+ ctor public WhitePoint(float x, float y);
+ ctor public WhitePoint(float x, float y, float z);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.colorspace.WhitePoint copy(float x, float y);
+ method public float getX();
+ method public float getY();
+ property public final float x;
+ property public final float y;
+ }
+
+}
+
+package androidx.compose.ui.graphics.drawscope {
+
+ public final class CanvasDrawScope implements androidx.compose.ui.graphics.drawscope.DrawScope {
+ ctor public CanvasDrawScope();
+ method public inline void draw(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.graphics.Canvas canvas, long size, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public void drawArc(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawArc(long color, float startAngle, float sweepAngle, boolean useCenter, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawCircle(androidx.compose.ui.graphics.Brush brush, float radius, long center, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawCircle(long color, float radius, long center, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawImage(androidx.compose.ui.graphics.ImageBitmap image, long topLeft, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method @Deprecated public void drawImage(androidx.compose.ui.graphics.ImageBitmap image, long srcOffset, long srcSize, long dstOffset, long dstSize, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawLine(androidx.compose.ui.graphics.Brush brush, long start, long end, float strokeWidth, int cap, androidx.compose.ui.graphics.PathEffect? pathEffect, float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawLine(long color, long start, long end, float strokeWidth, int cap, androidx.compose.ui.graphics.PathEffect? pathEffect, float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawOval(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawOval(long color, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawPath(androidx.compose.ui.graphics.Path path, long color, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, int pointMode, androidx.compose.ui.graphics.Brush brush, float strokeWidth, int cap, androidx.compose.ui.graphics.PathEffect? pathEffect, float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, int pointMode, long color, float strokeWidth, int cap, androidx.compose.ui.graphics.PathEffect? pathEffect, float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawRect(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawRect(long color, long topLeft, long size, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawRoundRect(androidx.compose.ui.graphics.Brush brush, long topLeft, long size, long cornerRadius, float alpha, androidx.compose.ui.graphics.drawscope.DrawStyle style, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public void drawRoundRect(long color, long topLeft, long size, long cornerRadius, androidx.compose.ui.graphics.drawscope.DrawStyle style, float alpha, androidx.compose.ui.graphics.ColorFilter? colorFilter, int blendMode);
+ method public float getDensity();
+ method public androidx.compose.ui.graphics.drawscope.DrawContext getDrawContext();
+ method public float getFontScale();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ property public float density;
+ property public androidx.compose.ui.graphics.drawscope.DrawContext drawContext;
+ property public float fontScale;
+ property public androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ContentDrawScope extends androidx.compose.ui.graphics.drawscope.DrawScope {
+ method public void drawContent();
+ }
+
+ public interface DrawContext {
+ method public androidx.compose.ui.graphics.Canvas getCanvas();
+ method public long getSize();
+ method public androidx.compose.ui.graphics.drawscope.DrawTransform getTransform();
+ method public void setSize(long);
+ property public abstract androidx.compose.ui.graphics.Canvas canvas;
+ property public abstract long size;
+ property public abstract androidx.compose.ui.graphics.drawscope.DrawTransform transform;
+ }
+
+ @androidx.compose.ui.graphics.drawscope.DrawScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface DrawScope extends androidx.compose.ui.unit.Density {
+ method public void drawArc(androidx.compose.ui.graphics.Brush brush, float startAngle, float sweepAngle, boolean useCenter, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawArc(long color, float startAngle, float sweepAngle, boolean useCenter, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawCircle(androidx.compose.ui.graphics.Brush brush, optional float radius, optional long center, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawCircle(long color, optional float radius, optional long center, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawImage(androidx.compose.ui.graphics.ImageBitmap image, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method @Deprecated public void drawImage(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public default void drawImage(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional long dstOffset, optional long dstSize, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode, optional int filterQuality);
+ method public void drawLine(androidx.compose.ui.graphics.Brush brush, long start, long end, optional float strokeWidth, optional int cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawLine(long color, long start, long end, optional float strokeWidth, optional int cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawOval(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawOval(long color, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawPath(androidx.compose.ui.graphics.Path path, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawPath(androidx.compose.ui.graphics.Path path, long color, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, int pointMode, androidx.compose.ui.graphics.Brush brush, optional float strokeWidth, optional int cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawPoints(java.util.List<androidx.compose.ui.geometry.Offset> points, int pointMode, long color, optional float strokeWidth, optional int cap, optional androidx.compose.ui.graphics.PathEffect? pathEffect, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawRect(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawRect(long color, optional long topLeft, optional long size, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawRoundRect(androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional long size, optional long cornerRadius, optional float alpha, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public void drawRoundRect(long color, optional long topLeft, optional long size, optional long cornerRadius, optional androidx.compose.ui.graphics.drawscope.DrawStyle style, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter, optional int blendMode);
+ method public default long getCenter();
+ method public androidx.compose.ui.graphics.drawscope.DrawContext getDrawContext();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method public default long getSize();
+ property public default long center;
+ property public abstract androidx.compose.ui.graphics.drawscope.DrawContext drawContext;
+ property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ property public default long size;
+ field public static final androidx.compose.ui.graphics.drawscope.DrawScope.Companion Companion;
+ }
+
+ public static final class DrawScope.Companion {
+ method public int getDefaultBlendMode();
+ method public int getDefaultFilterQuality();
+ property public final int DefaultBlendMode;
+ property public final int DefaultFilterQuality;
+ }
+
+ public final class DrawScopeKt {
+ method public static inline void clipPath(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.graphics.Path path, optional int clipOp, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void clipRect(androidx.compose.ui.graphics.drawscope.DrawScope, optional float left, optional float top, optional float right, optional float bottom, optional int clipOp, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void drawIntoCanvas(androidx.compose.ui.graphics.drawscope.DrawScope, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.Canvas,kotlin.Unit> block);
+ method public static inline void inset(androidx.compose.ui.graphics.drawscope.DrawScope, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void inset(androidx.compose.ui.graphics.drawscope.DrawScope, optional float horizontal, optional float vertical, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void inset(androidx.compose.ui.graphics.drawscope.DrawScope, float inset, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void rotate(androidx.compose.ui.graphics.drawscope.DrawScope, float degrees, optional long pivot, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void rotateRad(androidx.compose.ui.graphics.drawscope.DrawScope, float radians, optional long pivot, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void scale(androidx.compose.ui.graphics.drawscope.DrawScope, float scaleX, float scaleY, optional long pivot, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void scale(androidx.compose.ui.graphics.drawscope.DrawScope, float scale, optional long pivot, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void translate(androidx.compose.ui.graphics.drawscope.DrawScope, optional float left, optional float top, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public static inline void withTransform(androidx.compose.ui.graphics.drawscope.DrawScope, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawTransform,kotlin.Unit> transformBlock, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> drawBlock);
+ }
+
+ @kotlin.DslMarker public @interface DrawScopeMarker {
+ }
+
+ public abstract sealed class DrawStyle {
+ }
+
+ @androidx.compose.ui.graphics.drawscope.DrawScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface DrawTransform {
+ method public void clipPath(androidx.compose.ui.graphics.Path path, optional int clipOp);
+ method public void clipRect(optional float left, optional float top, optional float right, optional float bottom, optional int clipOp);
+ method public default long getCenter();
+ method public long getSize();
+ method public void inset(float left, float top, float right, float bottom);
+ method public void rotate(float degrees, optional long pivot);
+ method public void scale(float scaleX, float scaleY, optional long pivot);
+ method public void transform(float[] matrix);
+ method public void translate(optional float left, optional float top);
+ property public default long center;
+ property public abstract long size;
+ }
+
+ public final class DrawTransformKt {
+ method public static inline void inset(androidx.compose.ui.graphics.drawscope.DrawTransform, float inset);
+ method public static inline void inset(androidx.compose.ui.graphics.drawscope.DrawTransform, optional float horizontal, optional float vertical);
+ method public static inline void rotateRad(androidx.compose.ui.graphics.drawscope.DrawTransform, float radians, optional long pivot);
+ method public static inline void scale(androidx.compose.ui.graphics.drawscope.DrawTransform, float scale, optional long pivot);
+ }
+
+ public final class Fill extends androidx.compose.ui.graphics.drawscope.DrawStyle {
+ field public static final androidx.compose.ui.graphics.drawscope.Fill INSTANCE;
+ }
+
+ public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
+ ctor public Stroke(optional float width, optional float miter, optional int cap, optional int join, optional androidx.compose.ui.graphics.PathEffect? pathEffect);
+ method public int getCap();
+ method public int getJoin();
+ method public float getMiter();
+ method public androidx.compose.ui.graphics.PathEffect? getPathEffect();
+ method public float getWidth();
+ property public final int cap;
+ property public final int join;
+ property public final float miter;
+ property public final androidx.compose.ui.graphics.PathEffect? pathEffect;
+ property public final float width;
+ field public static final androidx.compose.ui.graphics.drawscope.Stroke.Companion Companion;
+ field public static final float DefaultMiter = 4.0f;
+ field public static final float HairlineWidth = 0.0f;
+ }
+
+ public static final class Stroke.Companion {
+ method public int getDefaultCap();
+ method public int getDefaultJoin();
+ property public final int DefaultCap;
+ property public final int DefaultJoin;
+ }
+
+}
+
+package androidx.compose.ui.graphics.painter {
+
+ public final class BitmapPainter extends androidx.compose.ui.graphics.painter.Painter {
+ ctor public BitmapPainter(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize);
+ method public long getIntrinsicSize();
+ method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ property public long intrinsicSize;
+ }
+
+ public final class BitmapPainterKt {
+ method public static androidx.compose.ui.graphics.painter.BitmapPainter BitmapPainter(androidx.compose.ui.graphics.ImageBitmap image, optional long srcOffset, optional long srcSize, optional int filterQuality);
+ }
+
+ public final class BrushPainter extends androidx.compose.ui.graphics.painter.Painter {
+ ctor public BrushPainter(androidx.compose.ui.graphics.Brush brush);
+ method public androidx.compose.ui.graphics.Brush getBrush();
+ method public long getIntrinsicSize();
+ method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ property public final androidx.compose.ui.graphics.Brush brush;
+ property public long intrinsicSize;
+ }
+
+ public final class ColorPainter extends androidx.compose.ui.graphics.painter.Painter {
+ ctor public ColorPainter(long color);
+ method public long getColor();
+ method public long getIntrinsicSize();
+ method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ property public final long color;
+ property public long intrinsicSize;
+ }
+
+ public abstract class Painter {
+ ctor public Painter();
+ method protected boolean applyAlpha(float alpha);
+ method protected boolean applyColorFilter(androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ method protected boolean applyLayoutDirection(androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public final void draw(androidx.compose.ui.graphics.drawscope.DrawScope, long size, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ method public abstract long getIntrinsicSize();
+ method protected abstract void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ property public abstract long intrinsicSize;
+ }
+
+}
+
+package androidx.compose.ui.graphics.vector {
+
+ public final class PathBuilder {
+ ctor public PathBuilder();
+ method public androidx.compose.ui.graphics.vector.PathBuilder arcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float x1, float y1);
+ method public androidx.compose.ui.graphics.vector.PathBuilder arcToRelative(float a, float b, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float dx1, float dy1);
+ method public androidx.compose.ui.graphics.vector.PathBuilder close();
+ method public androidx.compose.ui.graphics.vector.PathBuilder curveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public androidx.compose.ui.graphics.vector.PathBuilder curveToRelative(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public java.util.List<androidx.compose.ui.graphics.vector.PathNode> getNodes();
+ method public androidx.compose.ui.graphics.vector.PathBuilder horizontalLineTo(float x);
+ method public androidx.compose.ui.graphics.vector.PathBuilder horizontalLineToRelative(float dx);
+ method public androidx.compose.ui.graphics.vector.PathBuilder lineTo(float x, float y);
+ method public androidx.compose.ui.graphics.vector.PathBuilder lineToRelative(float dx, float dy);
+ method public androidx.compose.ui.graphics.vector.PathBuilder moveTo(float x, float y);
+ method public androidx.compose.ui.graphics.vector.PathBuilder moveToRelative(float dx, float dy);
+ method public androidx.compose.ui.graphics.vector.PathBuilder quadTo(float x1, float y1, float x2, float y2);
+ method public androidx.compose.ui.graphics.vector.PathBuilder quadToRelative(float dx1, float dy1, float dx2, float dy2);
+ method public androidx.compose.ui.graphics.vector.PathBuilder reflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public androidx.compose.ui.graphics.vector.PathBuilder reflectiveCurveToRelative(float dx1, float dy1, float dx2, float dy2);
+ method public androidx.compose.ui.graphics.vector.PathBuilder reflectiveQuadTo(float x1, float y1);
+ method public androidx.compose.ui.graphics.vector.PathBuilder reflectiveQuadToRelative(float dx1, float dy1);
+ method public androidx.compose.ui.graphics.vector.PathBuilder verticalLineTo(float y);
+ method public androidx.compose.ui.graphics.vector.PathBuilder verticalLineToRelative(float dy);
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class PathNode {
+ method public final boolean isCurve();
+ method public final boolean isQuad();
+ property public final boolean isCurve;
+ property public final boolean isQuad;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.ArcTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.ArcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float arcStartX, float arcStartY);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public boolean component4();
+ method public boolean component5();
+ method public float component6();
+ method public float component7();
+ method public androidx.compose.ui.graphics.vector.PathNode.ArcTo copy(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float arcStartX, float arcStartY);
+ method public float getArcStartX();
+ method public float getArcStartY();
+ method public float getHorizontalEllipseRadius();
+ method public float getTheta();
+ method public float getVerticalEllipseRadius();
+ method public boolean isMoreThanHalf();
+ method public boolean isPositiveArc();
+ property public final float arcStartX;
+ property public final float arcStartY;
+ property public final float horizontalEllipseRadius;
+ property public final boolean isMoreThanHalf;
+ property public final boolean isPositiveArc;
+ property public final float theta;
+ property public final float verticalEllipseRadius;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.Close extends androidx.compose.ui.graphics.vector.PathNode {
+ field public static final androidx.compose.ui.graphics.vector.PathNode.Close INSTANCE;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.CurveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.CurveTo(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public float component5();
+ method public float component6();
+ method public androidx.compose.ui.graphics.vector.PathNode.CurveTo copy(float x1, float y1, float x2, float y2, float x3, float y3);
+ method public float getX1();
+ method public float getX2();
+ method public float getX3();
+ method public float getY1();
+ method public float getY2();
+ method public float getY3();
+ property public final float x1;
+ property public final float x2;
+ property public final float x3;
+ property public final float y1;
+ property public final float y2;
+ property public final float y3;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.HorizontalTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.HorizontalTo(float x);
+ method public float component1();
+ method public androidx.compose.ui.graphics.vector.PathNode.HorizontalTo copy(float x);
+ method public float getX();
+ property public final float x;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.LineTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.LineTo(float x, float y);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.LineTo copy(float x, float y);
+ method public float getX();
+ method public float getY();
+ property public final float x;
+ property public final float y;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.MoveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.MoveTo(float x, float y);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.MoveTo copy(float x, float y);
+ method public float getX();
+ method public float getY();
+ property public final float x;
+ property public final float y;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.QuadTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.QuadTo(float x1, float y1, float x2, float y2);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public androidx.compose.ui.graphics.vector.PathNode.QuadTo copy(float x1, float y1, float x2, float y2);
+ method public float getX1();
+ method public float getX2();
+ method public float getY1();
+ method public float getY2();
+ property public final float x1;
+ property public final float x2;
+ property public final float y1;
+ property public final float y2;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.ReflectiveCurveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.ReflectiveCurveTo(float x1, float y1, float x2, float y2);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public androidx.compose.ui.graphics.vector.PathNode.ReflectiveCurveTo copy(float x1, float y1, float x2, float y2);
+ method public float getX1();
+ method public float getX2();
+ method public float getY1();
+ method public float getY2();
+ property public final float x1;
+ property public final float x2;
+ property public final float y1;
+ property public final float y2;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.ReflectiveQuadTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.ReflectiveQuadTo(float x, float y);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.ReflectiveQuadTo copy(float x, float y);
+ method public float getX();
+ method public float getY();
+ property public final float x;
+ property public final float y;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeArcTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeArcTo(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float arcStartDx, float arcStartDy);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public boolean component4();
+ method public boolean component5();
+ method public float component6();
+ method public float component7();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeArcTo copy(float horizontalEllipseRadius, float verticalEllipseRadius, float theta, boolean isMoreThanHalf, boolean isPositiveArc, float arcStartDx, float arcStartDy);
+ method public float getArcStartDx();
+ method public float getArcStartDy();
+ method public float getHorizontalEllipseRadius();
+ method public float getTheta();
+ method public float getVerticalEllipseRadius();
+ method public boolean isMoreThanHalf();
+ method public boolean isPositiveArc();
+ property public final float arcStartDx;
+ property public final float arcStartDy;
+ property public final float horizontalEllipseRadius;
+ property public final boolean isMoreThanHalf;
+ property public final boolean isPositiveArc;
+ property public final float theta;
+ property public final float verticalEllipseRadius;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeCurveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeCurveTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public float component5();
+ method public float component6();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeCurveTo copy(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
+ method public float getDx1();
+ method public float getDx2();
+ method public float getDx3();
+ method public float getDy1();
+ method public float getDy2();
+ method public float getDy3();
+ property public final float dx1;
+ property public final float dx2;
+ property public final float dx3;
+ property public final float dy1;
+ property public final float dy2;
+ property public final float dy3;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeHorizontalTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeHorizontalTo(float dx);
+ method public float component1();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeHorizontalTo copy(float dx);
+ method public float getDx();
+ property public final float dx;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeLineTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeLineTo(float dx, float dy);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeLineTo copy(float dx, float dy);
+ method public float getDx();
+ method public float getDy();
+ property public final float dx;
+ property public final float dy;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeMoveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeMoveTo(float dx, float dy);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeMoveTo copy(float dx, float dy);
+ method public float getDx();
+ method public float getDy();
+ property public final float dx;
+ property public final float dy;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeQuadTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeQuadTo(float dx1, float dy1, float dx2, float dy2);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeQuadTo copy(float dx1, float dy1, float dx2, float dy2);
+ method public float getDx1();
+ method public float getDx2();
+ method public float getDy1();
+ method public float getDy2();
+ property public final float dx1;
+ property public final float dx2;
+ property public final float dy1;
+ property public final float dy2;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeReflectiveCurveTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeReflectiveCurveTo(float dx1, float dy1, float dx2, float dy2);
+ method public float component1();
+ method public float component2();
+ method public float component3();
+ method public float component4();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeReflectiveCurveTo copy(float dx1, float dy1, float dx2, float dy2);
+ method public float getDx1();
+ method public float getDx2();
+ method public float getDy1();
+ method public float getDy2();
+ property public final float dx1;
+ property public final float dx2;
+ property public final float dy1;
+ property public final float dy2;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeReflectiveQuadTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeReflectiveQuadTo(float dx, float dy);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeReflectiveQuadTo copy(float dx, float dy);
+ method public float getDx();
+ method public float getDy();
+ property public final float dx;
+ property public final float dy;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.RelativeVerticalTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.RelativeVerticalTo(float dy);
+ method public float component1();
+ method public androidx.compose.ui.graphics.vector.PathNode.RelativeVerticalTo copy(float dy);
+ method public float getDy();
+ property public final float dy;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class PathNode.VerticalTo extends androidx.compose.ui.graphics.vector.PathNode {
+ ctor public PathNode.VerticalTo(float y);
+ method public float component1();
+ method public androidx.compose.ui.graphics.vector.PathNode.VerticalTo copy(float y);
+ method public float getY();
+ property public final float y;
+ }
+
+ public final class PathParser {
+ ctor public PathParser();
+ method public androidx.compose.ui.graphics.vector.PathParser addPathNodes(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> nodes);
+ method public void clear();
+ method public androidx.compose.ui.graphics.vector.PathParser parsePathString(String pathData);
+ method public java.util.List<androidx.compose.ui.graphics.vector.PathNode> toNodes();
+ method public androidx.compose.ui.graphics.Path toPath(optional androidx.compose.ui.graphics.Path target);
+ }
+
+ public final class PathParserKt {
+ method public static androidx.compose.ui.graphics.Path toPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>, optional androidx.compose.ui.graphics.Path target);
+ }
+
+}
+
diff --git a/compose/ui/ui-graphics/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-graphics/api/restricted_1.5.0-beta01.txt
index 4aa03ab..33e27b1 100644
--- a/compose/ui/ui-graphics/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-graphics/api/restricted_1.5.0-beta01.txt
@@ -439,9 +439,6 @@
method @kotlin.PublishedApi internal static float degrees(float radians);
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGraphicsApi {
- }
-
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class FilterQuality {
method public int getValue();
property public final int value;
diff --git a/compose/ui/ui-test-junit4/api/1.5.0-beta01.txt b/compose/ui/ui-test-junit4/api/1.5.0-beta01.txt
index 8f10178..2d6528b 100644
--- a/compose/ui/ui-test-junit4/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-test-junit4/api/1.5.0-beta01.txt
@@ -1,63 +1,7 @@
// Signature format: 4.0
-package androidx.compose.ui.test {
-
- @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
- method public A? getActivity();
- property public abstract A? activity;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
- ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
- method protected abstract A? getActivity();
- method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
- method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
- property protected abstract A? activity;
- property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
- method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.ui.unit.Density getDensity();
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public void waitForIdle();
- method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- property public abstract androidx.compose.ui.unit.Density density;
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class ComposeUiTestKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
- }
-
- public final class ComposeUiTest_androidKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
- method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
- ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
- method public void emulateSaveAndRestore();
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- }
-
-}
-
package androidx.compose.ui.test.junit4 {
public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeContentTestRule {
- ctor @androidx.compose.ui.test.ExperimentalTestApi public AndroidComposeTestRule(R activityRule, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -74,10 +18,6 @@
method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
method public void waitForIdle();
method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis);
property public final A activity;
property public final R activityRule;
property public androidx.compose.ui.unit.Density density;
@@ -92,12 +32,8 @@
public final class AndroidComposeTestRule_androidKt {
method public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule();
method public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass);
- method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext);
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
method public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule();
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule(kotlin.coroutines.CoroutineContext effectContext);
method public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule();
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface ComposeContentTestRule extends androidx.compose.ui.test.junit4.ComposeTestRule {
@@ -114,10 +50,6 @@
method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
method public void waitForIdle();
method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
property public abstract androidx.compose.ui.unit.Density density;
property public abstract androidx.compose.ui.test.MainTestClock mainClock;
}
diff --git a/compose/ui/ui-test-junit4/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-test-junit4/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..8f10178
--- /dev/null
+++ b/compose/ui/ui-test-junit4/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,140 @@
+// Signature format: 4.0
+package androidx.compose.ui.test {
+
+ @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
+ method public A? getActivity();
+ property public abstract A? activity;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
+ ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
+ method protected abstract A? getActivity();
+ method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
+ method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
+ property protected abstract A? activity;
+ property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public void waitForIdle();
+ method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ public final class ComposeUiTestKt {
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
+ }
+
+ public final class ComposeUiTest_androidKt {
+ method @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
+ ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
+ method public void emulateSaveAndRestore();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ }
+
+}
+
+package androidx.compose.ui.test.junit4 {
+
+ public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeContentTestRule {
+ ctor @androidx.compose.ui.test.ExperimentalTestApi public AndroidComposeTestRule(R activityRule, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
+ ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
+ method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public A getActivity();
+ method public R getActivityRule();
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
+ method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, boolean useUnmergedTree);
+ method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public void waitForIdle();
+ method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis);
+ property public final A activity;
+ property public final R activityRule;
+ property public androidx.compose.ui.unit.Density density;
+ property public androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ @Deprecated public final class AndroidComposeTestRule.AndroidComposeStatement extends org.junit.runners.model.Statement {
+ ctor @Deprecated public AndroidComposeTestRule.AndroidComposeStatement(org.junit.runners.model.Statement base);
+ method @Deprecated public void evaluate();
+ }
+
+ public final class AndroidComposeTestRule_androidKt {
+ method public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule();
+ method public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
+ method public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule();
+ method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule(kotlin.coroutines.CoroutineContext effectContext);
+ method public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule();
+ method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ComposeContentTestRule extends androidx.compose.ui.test.junit4.ComposeTestRule {
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ComposeTestRule extends org.junit.rules.TestRule androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
+ method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
+ method public void waitForIdle();
+ method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ public final class StateRestorationTester {
+ ctor public StateRestorationTester(androidx.compose.ui.test.junit4.ComposeContentTestRule composeTestRule);
+ method public void emulateSavedInstanceStateRestore();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ }
+
+}
+
+package androidx.compose.ui.test.junit4.android {
+
+ 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.5.0-beta01.txt b/compose/ui/ui-test-junit4/api/restricted_1.5.0-beta01.txt
index 8f10178..2d6528b 100644
--- a/compose/ui/ui-test-junit4/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_1.5.0-beta01.txt
@@ -1,63 +1,7 @@
// Signature format: 4.0
-package androidx.compose.ui.test {
-
- @androidx.compose.ui.test.ExperimentalTestApi public sealed interface AndroidComposeUiTest<A extends androidx.activity.ComponentActivity> extends androidx.compose.ui.test.ComposeUiTest {
- method public A? getActivity();
- property public abstract A? activity;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
- ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
- method protected abstract A? getActivity();
- method public final androidx.compose.ui.test.AndroidComposeUiTest<A> getTest();
- method public final <R> R runTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,? extends R> block);
- property protected abstract A? activity;
- property public final androidx.compose.ui.test.AndroidComposeUiTest<A> test;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public sealed interface ComposeUiTest extends androidx.compose.ui.test.SemanticsNodeInteractionsProvider {
- method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public androidx.compose.ui.unit.Density getDensity();
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public void registerIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public <T> T runOnIdle(kotlin.jvm.functions.Function0<? extends T> action);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
- method public void waitForIdle();
- method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- property public abstract androidx.compose.ui.unit.Density density;
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class ComposeUiTestKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilAtLeastOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilDoesNotExist(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilExactlyOneExists(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void waitUntilNodeCount(androidx.compose.ui.test.ComposeUiTest, androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
- }
-
- public final class ComposeUiTest_androidKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.AndroidComposeUiTestEnvironment<A> AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function0<? extends A> activityProvider);
- method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> void runAndroidComposeUiTest(optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.AndroidComposeUiTest<A>,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runComposeUiTest(kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void runEmptyComposeUiTest(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.ComposeUiTest,kotlin.Unit> block);
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public final class StateRestorationTester {
- ctor public StateRestorationTester(androidx.compose.ui.test.ComposeUiTest composeTest);
- method public void emulateSaveAndRestore();
- method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- }
-
-}
-
package androidx.compose.ui.test.junit4 {
public final class AndroidComposeTestRule<R extends org.junit.rules.TestRule, A extends androidx.activity.ComponentActivity> implements androidx.compose.ui.test.junit4.ComposeContentTestRule {
- ctor @androidx.compose.ui.test.ExperimentalTestApi public AndroidComposeTestRule(R activityRule, optional kotlin.coroutines.CoroutineContext effectContext, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
ctor public AndroidComposeTestRule(R activityRule, kotlin.jvm.functions.Function1<? super R,? extends A> activityProvider);
method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -74,10 +18,6 @@
method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
method public void waitForIdle();
method public void waitUntil(long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, long timeoutMillis);
property public final A activity;
property public final R activityRule;
property public androidx.compose.ui.unit.Density density;
@@ -92,12 +32,8 @@
public final class AndroidComposeTestRule_androidKt {
method public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule();
method public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass);
- method @androidx.compose.ui.test.ExperimentalTestApi public static <A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(Class<A> activityClass, optional kotlin.coroutines.CoroutineContext effectContext);
- method @androidx.compose.ui.test.ExperimentalTestApi public static inline <reified A extends androidx.activity.ComponentActivity> androidx.compose.ui.test.junit4.AndroidComposeTestRule<androidx.test.ext.junit.rules.ActivityScenarioRule<A>,A> createAndroidComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
method public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule();
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeContentTestRule createComposeRule(kotlin.coroutines.CoroutineContext effectContext);
method public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule();
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.junit4.ComposeTestRule createEmptyComposeRule(optional kotlin.coroutines.CoroutineContext effectContext);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface ComposeContentTestRule extends androidx.compose.ui.test.junit4.ComposeTestRule {
@@ -114,10 +50,6 @@
method public void unregisterIdlingResource(androidx.compose.ui.test.IdlingResource idlingResource);
method public void waitForIdle();
method public void waitUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilAtLeastOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilDoesNotExist(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilExactlyOneExists(androidx.compose.ui.test.SemanticsMatcher matcher, optional long timeoutMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void waitUntilNodeCount(androidx.compose.ui.test.SemanticsMatcher matcher, int count, optional long timeoutMillis);
property public abstract androidx.compose.ui.unit.Density density;
property public abstract androidx.compose.ui.test.MainTestClock mainClock;
}
diff --git a/compose/ui/ui-test-manifest/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-test-manifest/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/compose/ui/ui-test-manifest/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/compose/ui/ui-test/api/1.5.0-beta01.txt b/compose/ui/ui-test/api/1.5.0-beta01.txt
index 1060ce5..05d9e4e 100644
--- a/compose/ui/ui-test/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-test/api/1.5.0-beta01.txt
@@ -4,10 +4,7 @@
public final class ActionsKt {
method public static androidx.compose.ui.test.SemanticsNodeInteraction performClick(androidx.compose.ui.test.SemanticsNodeInteraction);
method @Deprecated public static androidx.compose.ui.test.SemanticsNodeInteraction performGesture(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.GestureScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performKeyInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performMouseInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performMultiModalInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MultiModalInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performRotaryScrollInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollTo(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToIndex(androidx.compose.ui.test.SemanticsNodeInteraction, int index);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToKey(androidx.compose.ui.test.SemanticsNodeInteraction, Object key);
@@ -70,9 +67,6 @@
ctor public ComposeTimeoutException(String? message);
}
- @kotlin.RequiresOptIn(message="This testing API is experimental and is likely to be changed or removed entirely") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTestApi {
- }
-
public final class FiltersKt {
method public static androidx.compose.ui.test.SemanticsMatcher hasAnyAncestor(androidx.compose.ui.test.SemanticsMatcher matcher);
method public static androidx.compose.ui.test.SemanticsMatcher hasAnyChild(androidx.compose.ui.test.SemanticsMatcher matcher);
@@ -133,7 +127,6 @@
}
public final class GestureScopeKt {
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void advanceEventTime(androidx.compose.ui.test.GestureScope, long durationMillis);
method @Deprecated public static void cancel(androidx.compose.ui.test.GestureScope);
method @Deprecated public static void click(androidx.compose.ui.test.GestureScope, optional long position);
method @Deprecated public static void doubleClick(androidx.compose.ui.test.GestureScope, optional long position, optional long delayMillis);
@@ -168,13 +161,9 @@
method @Deprecated public static void pinch(androidx.compose.ui.test.GestureScope, long start0, long end0, long start1, long end1, optional long durationMillis);
method @Deprecated public static void swipe(androidx.compose.ui.test.GestureScope, long start, long end, optional long durationMillis);
method @Deprecated public static void swipeDown(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeDown(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
method @Deprecated public static void swipeLeft(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeLeft(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
method @Deprecated public static void swipeRight(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeRight(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
method @Deprecated public static void swipeUp(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeUp(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
method @Deprecated public static void swipeWithVelocity(androidx.compose.ui.test.GestureScope, long start, long end, float endVelocity, optional long durationMillis);
method @Deprecated public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
}
@@ -230,9 +219,6 @@
property public default int width;
}
- @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalTestApi {
- }
-
@kotlin.jvm.JvmDefaultWithCompatibility public interface KeyInjectionScope extends androidx.compose.ui.test.InjectionScope {
method public boolean isCapsLockOn();
method public boolean isKeyDown(long key);
@@ -273,54 +259,7 @@
property public abstract long currentTime;
}
- @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class MouseButton {
- ctor public MouseButton(int buttonId);
- method public int getButtonId();
- property public final int buttonId;
- field public static final androidx.compose.ui.test.MouseButton.Companion Companion;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public static final class MouseButton.Companion {
- method public int getPrimary();
- method public int getSecondary();
- method public int getTertiary();
- property public final int Primary;
- property public final int Secondary;
- property public final int Tertiary;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public interface MouseInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method public void cancel(optional long delayMillis);
- method public void enter(optional long position, optional long delayMillis);
- method public void exit(optional long position, optional long delayMillis);
- method public long getCurrentPosition();
- method public default void moveBy(long delta, optional long delayMillis);
- method public void moveTo(long position, optional long delayMillis);
- method public void press(optional int button);
- method public void release(optional int button);
- method public void scroll(float delta, optional int scrollWheel);
- method public default void updatePointerBy(long delta);
- method public void updatePointerTo(long position);
- property public abstract long currentPosition;
- }
-
- public final class MouseInjectionScopeKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateAlong(androidx.compose.ui.test.MouseInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateBy(androidx.compose.ui.test.MouseInjectionScope, long delta, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateTo(androidx.compose.ui.test.MouseInjectionScope, long position, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void click(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void doubleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void dragAndDrop(androidx.compose.ui.test.MouseInjectionScope, long start, long end, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void longClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void rightClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void smoothScroll(androidx.compose.ui.test.MouseInjectionScope, float scrollAmount, optional long durationMillis, optional int scrollWheel);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void tripleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- }
-
public sealed interface MultiModalInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method @androidx.compose.ui.test.ExperimentalTestApi public void key(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public void mouse(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public void rotary(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
method public void touch(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.TouchInjectionScope,kotlin.Unit> block);
}
@@ -331,24 +270,6 @@
method public static String printToString(androidx.compose.ui.test.SemanticsNodeInteractionCollection, optional int maxDepth);
}
- @androidx.compose.ui.test.ExperimentalTestApi public interface RotaryInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method public void rotateToScrollHorizontally(float horizontalScrollPixels);
- method public void rotateToScrollVertically(float verticalScrollPixels);
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class ScrollWheel {
- method public int getValue();
- property public final int value;
- field public static final androidx.compose.ui.test.ScrollWheel.Companion Companion;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public static final class ScrollWheel.Companion {
- method public int getHorizontal();
- method public int getVertical();
- property public final int Horizontal;
- property public final int Vertical;
- }
-
public final class SelectionResult {
ctor public SelectionResult(java.util.List<androidx.compose.ui.semantics.SemanticsNode> selectedNodes, optional String? customErrorOnNoMatch);
method public String? getCustomErrorOnNoMatch();
@@ -419,37 +340,10 @@
public final class TestContext {
}
- @androidx.compose.ui.test.ExperimentalTestApi @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
- ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, optional long frameDelayNanos, optional kotlin.jvm.functions.Function1<? super java.lang.Long,kotlin.Unit> onPerformTraversals);
- method @androidx.compose.ui.test.ExperimentalTestApi public kotlin.coroutines.ContinuationInterceptor getContinuationInterceptor();
- method public long getFrameDelayNanos();
- method public boolean getHasAwaiters();
- method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
- property @androidx.compose.ui.test.ExperimentalTestApi public final kotlin.coroutines.ContinuationInterceptor continuationInterceptor;
- property public final long frameDelayNanos;
- property public final boolean hasAwaiters;
- }
-
- public final class TestMonotonicFrameClock_jvmKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static long getFrameDelayMillis(androidx.compose.ui.test.TestMonotonicFrameClock);
- }
-
- @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots(boolean atLeastOneRootExpected);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class TestOwnerKt {
- method @androidx.compose.ui.test.InternalTestApi public static androidx.compose.ui.test.TestContext createTestContext(androidx.compose.ui.test.TestOwner owner);
- }
-
public final class TextActionsKt {
method public static void performImeAction(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static void performTextClearance(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static void performTextInput(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void performTextInputSelection(androidx.compose.ui.test.SemanticsNodeInteraction, long selection);
method public static void performTextReplacement(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
}
@@ -463,8 +357,6 @@
method public default void moveBy(long delta, optional long delayMillis);
method public default void moveTo(int pointerId, long position, optional long delayMillis);
method public default void moveTo(long position, optional long delayMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public default void moveWithHistory(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<androidx.compose.ui.geometry.Offset> historicalCoordinates, optional long delayMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void moveWithHistoryMultiPointer(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<? extends java.util.List<androidx.compose.ui.geometry.Offset>> historicalCoordinates, optional long delayMillis);
method public void up(optional int pointerId);
method public default void updatePointerBy(int pointerId, long delta);
method public void updatePointerTo(int pointerId, long position);
@@ -474,7 +366,6 @@
method public static void click(androidx.compose.ui.test.TouchInjectionScope, optional long position);
method public static void doubleClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long delayMillis);
method public static void longClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void pinch(androidx.compose.ui.test.TouchInjectionScope, long start0, long end0, long start1, long end1, optional long durationMillis);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, long start, long end, optional long durationMillis);
@@ -487,11 +378,3 @@
}
-package androidx.compose.ui.test.internal {
-
- @androidx.compose.ui.test.InternalTestApi public abstract class DelayPropagatingContinuationInterceptorWrapper extends kotlin.coroutines.AbstractCoroutineContextElement implements kotlin.coroutines.ContinuationInterceptor kotlinx.coroutines.Delay {
- ctor public DelayPropagatingContinuationInterceptorWrapper(kotlin.coroutines.ContinuationInterceptor? wrappedInterceptor);
- }
-
-}
-
diff --git a/compose/ui/ui-test/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-test/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..1060ce5
--- /dev/null
+++ b/compose/ui/ui-test/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,497 @@
+// Signature format: 4.0
+package androidx.compose.ui.test {
+
+ public final class ActionsKt {
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performClick(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method @Deprecated public static androidx.compose.ui.test.SemanticsNodeInteraction performGesture(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.GestureScope,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performKeyInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performMouseInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performMultiModalInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MultiModalInjectionScope,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performRotaryScrollInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollTo(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToIndex(androidx.compose.ui.test.SemanticsNodeInteraction, int index);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToKey(androidx.compose.ui.test.SemanticsNodeInteraction, Object key);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToNode(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performSemanticsAction(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> key);
+ method @Deprecated public static void performSemanticsAction(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> key);
+ method public static <T extends kotlin.Function<? extends java.lang.Boolean>> androidx.compose.ui.test.SemanticsNodeInteraction performSemanticsAction(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<T>> key, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> invocation);
+ method @Deprecated public static <T extends kotlin.Function<? extends java.lang.Boolean>> void performSemanticsAction(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<T>> key, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> invocation);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction performTouchInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.TouchInjectionScope,kotlin.Unit> block);
+ }
+
+ public final class AndroidImageHelpers_androidKt {
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static androidx.compose.ui.graphics.ImageBitmap captureToImage(androidx.compose.ui.test.SemanticsNodeInteraction);
+ }
+
+ public final class AssertionsKt {
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assert(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.test.SemanticsMatcher matcher, optional kotlin.jvm.functions.Function0<java.lang.String>? messagePrefixOnError);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection assertAll(androidx.compose.ui.test.SemanticsNodeInteractionCollection, androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection assertAny(androidx.compose.ui.test.SemanticsNodeInteractionCollection, androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertContentDescriptionContains(androidx.compose.ui.test.SemanticsNodeInteraction, String value, optional boolean substring, optional boolean ignoreCase);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertContentDescriptionEquals(androidx.compose.ui.test.SemanticsNodeInteraction, java.lang.String... values);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection assertCountEquals(androidx.compose.ui.test.SemanticsNodeInteractionCollection, int expectedSize);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertHasClickAction(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertHasNoClickAction(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsDisplayed(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsEnabled(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsFocused(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsNotDisplayed(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsNotEnabled(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsNotFocused(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsNotSelected(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsOff(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsOn(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsSelectable(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsSelected(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertIsToggleable(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertRangeInfoEquals(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.semantics.ProgressBarRangeInfo value);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertTextContains(androidx.compose.ui.test.SemanticsNodeInteraction, String value, optional boolean substring, optional boolean ignoreCase);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertTextEquals(androidx.compose.ui.test.SemanticsNodeInteraction, String![] values, optional boolean includeEditableText);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertValueEquals(androidx.compose.ui.test.SemanticsNodeInteraction, String value);
+ }
+
+ public final class BoundsAssertionsKt {
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertHeightIsAtLeast(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedMinHeight);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertHeightIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedHeight);
+ method public static void assertIsEqualTo(float, float expected, String subject, optional float tolerance);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertLeftPositionInRootIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedLeft);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertPositionInRootIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedLeft, float expectedTop);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertTopPositionInRootIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedTop);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertTouchHeightIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedHeight);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertTouchWidthIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedWidth);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertWidthIsAtLeast(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedMinWidth);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction assertWidthIsEqualTo(androidx.compose.ui.test.SemanticsNodeInteraction, float expectedWidth);
+ method public static float getAlignmentLinePosition(androidx.compose.ui.test.SemanticsNodeInteraction, androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public static androidx.compose.ui.unit.DpRect getBoundsInRoot(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.unit.DpRect getUnclippedBoundsInRoot(androidx.compose.ui.test.SemanticsNodeInteraction);
+ }
+
+ public final class ComposeTimeoutException extends java.lang.Throwable {
+ ctor public ComposeTimeoutException(String? message);
+ }
+
+ @kotlin.RequiresOptIn(message="This testing API is experimental and is likely to be changed or removed entirely") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTestApi {
+ }
+
+ public final class FiltersKt {
+ method public static androidx.compose.ui.test.SemanticsMatcher hasAnyAncestor(androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasAnyChild(androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasAnyDescendant(androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasAnySibling(androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasClickAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String value, optional boolean substring, optional boolean ignoreCase);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescriptionExactly(java.lang.String... values);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(int actionType);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasInsertTextAtCursorAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasPerformImeAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasProgressBarRangeInfo(androidx.compose.ui.semantics.ProgressBarRangeInfo rangeInfo);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasRequestFocusAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasScrollAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToIndexAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToKeyAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToNodeAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasSetTextAction();
+ method public static androidx.compose.ui.test.SemanticsMatcher hasStateDescription(String value);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasTestTag(String testTag);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasText(String text, optional boolean substring, optional boolean ignoreCase);
+ method public static androidx.compose.ui.test.SemanticsMatcher hasTextExactly(String![] textValues, optional boolean includeEditableText);
+ method public static androidx.compose.ui.test.SemanticsMatcher isDialog();
+ method public static androidx.compose.ui.test.SemanticsMatcher isEnabled();
+ method public static androidx.compose.ui.test.SemanticsMatcher isFocusable();
+ method public static androidx.compose.ui.test.SemanticsMatcher isFocused();
+ method public static androidx.compose.ui.test.SemanticsMatcher isHeading();
+ method public static androidx.compose.ui.test.SemanticsMatcher isNotEnabled();
+ method public static androidx.compose.ui.test.SemanticsMatcher isNotFocusable();
+ method public static androidx.compose.ui.test.SemanticsMatcher isNotFocused();
+ method public static androidx.compose.ui.test.SemanticsMatcher isNotSelected();
+ method public static androidx.compose.ui.test.SemanticsMatcher isOff();
+ method public static androidx.compose.ui.test.SemanticsMatcher isOn();
+ method public static androidx.compose.ui.test.SemanticsMatcher isPopup();
+ method public static androidx.compose.ui.test.SemanticsMatcher isRoot();
+ method public static androidx.compose.ui.test.SemanticsMatcher isSelectable();
+ method public static androidx.compose.ui.test.SemanticsMatcher isSelected();
+ method public static androidx.compose.ui.test.SemanticsMatcher isToggleable();
+ }
+
+ public final class FindersKt {
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean substring, optional boolean ignoreCase, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodesWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean substring, optional boolean ignoreCase, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithContentDescription(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String label, optional boolean substring, optional boolean ignoreCase, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithTag(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String testTag, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onNodeWithText(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, String text, optional boolean substring, optional boolean ignoreCase, optional boolean useUnmergedTree);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onRoot(androidx.compose.ui.test.SemanticsNodeInteractionsProvider, optional boolean useUnmergedTree);
+ }
+
+ @Deprecated public final class GestureScope {
+ ctor @Deprecated public GestureScope(androidx.compose.ui.semantics.SemanticsNode node, androidx.compose.ui.test.TestContext testContext);
+ method @Deprecated public long getVisibleSize();
+ property @Deprecated public final long visibleSize;
+ }
+
+ public final class GestureScopeKt {
+ method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void advanceEventTime(androidx.compose.ui.test.GestureScope, long durationMillis);
+ method @Deprecated public static void cancel(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static void click(androidx.compose.ui.test.GestureScope, optional long position);
+ method @Deprecated public static void doubleClick(androidx.compose.ui.test.GestureScope, optional long position, optional long delayMillis);
+ method @Deprecated public static void down(androidx.compose.ui.test.GestureScope, int pointerId, long position);
+ method @Deprecated public static void down(androidx.compose.ui.test.GestureScope, long position);
+ method @Deprecated public static inline float getBottom(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getBottomCenter(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getBottomLeft(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getBottomRight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getCenter(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getCenterLeft(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getCenterRight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline float getCenterX(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline float getCenterY(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline int getHeight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline float getLeft(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline float getRight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline float getTop(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getTopCenter(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getTopLeft(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static long getTopRight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static inline int getWidth(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static void longClick(androidx.compose.ui.test.GestureScope, optional long position, optional long durationMillis);
+ method @Deprecated public static void move(androidx.compose.ui.test.GestureScope);
+ method @Deprecated public static void moveBy(androidx.compose.ui.test.GestureScope, int pointerId, long delta);
+ method @Deprecated public static void moveBy(androidx.compose.ui.test.GestureScope, long delta);
+ method @Deprecated public static void movePointerBy(androidx.compose.ui.test.GestureScope, int pointerId, long delta);
+ method @Deprecated public static void movePointerTo(androidx.compose.ui.test.GestureScope, int pointerId, long position);
+ method @Deprecated public static void moveTo(androidx.compose.ui.test.GestureScope, int pointerId, long position);
+ method @Deprecated public static void moveTo(androidx.compose.ui.test.GestureScope, long position);
+ method @Deprecated public static long percentOffset(androidx.compose.ui.test.GestureScope, optional float x, optional float y);
+ method @Deprecated public static void pinch(androidx.compose.ui.test.GestureScope, long start0, long end0, long start1, long end1, optional long durationMillis);
+ method @Deprecated public static void swipe(androidx.compose.ui.test.GestureScope, long start, long end, optional long durationMillis);
+ method @Deprecated public static void swipeDown(androidx.compose.ui.test.GestureScope);
+ method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeDown(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
+ method @Deprecated public static void swipeLeft(androidx.compose.ui.test.GestureScope);
+ method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeLeft(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
+ method @Deprecated public static void swipeRight(androidx.compose.ui.test.GestureScope);
+ method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeRight(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
+ method @Deprecated public static void swipeUp(androidx.compose.ui.test.GestureScope);
+ method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeUp(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
+ method @Deprecated public static void swipeWithVelocity(androidx.compose.ui.test.GestureScope, long start, long end, float endVelocity, optional long durationMillis);
+ method @Deprecated public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface IdlingResource {
+ method public default String? getDiagnosticMessageIfBusy();
+ method public boolean isIdleNow();
+ property public abstract boolean isIdleNow;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface InjectionScope extends androidx.compose.ui.unit.Density {
+ method public void advanceEventTime(optional long durationMillis);
+ method public default float getBottom();
+ method public default long getBottomCenter();
+ method public default long getBottomLeft();
+ method public default long getBottomRight();
+ method public default long getCenter();
+ method public default long getCenterLeft();
+ method public default long getCenterRight();
+ method public default float getCenterX();
+ method public default float getCenterY();
+ method public default long getEventPeriodMillis();
+ method public default int getHeight();
+ method public default float getLeft();
+ method public default float getRight();
+ method public default float getTop();
+ method public default long getTopCenter();
+ method public default long getTopLeft();
+ method public default long getTopRight();
+ method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+ method public long getVisibleSize();
+ method public default int getWidth();
+ method public default long percentOffset(optional float x, optional float y);
+ property public default float bottom;
+ property public default long bottomCenter;
+ property public default long bottomLeft;
+ property public default long bottomRight;
+ property public default long center;
+ property public default long centerLeft;
+ property public default long centerRight;
+ property public default float centerX;
+ property public default float centerY;
+ property public default long eventPeriodMillis;
+ property public default int height;
+ property public default float left;
+ property public default float right;
+ property public default float top;
+ property public default long topCenter;
+ property public default long topLeft;
+ property public default long topRight;
+ property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+ property public abstract long visibleSize;
+ property public default int width;
+ }
+
+ @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalTestApi {
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface KeyInjectionScope extends androidx.compose.ui.test.InjectionScope {
+ method public boolean isCapsLockOn();
+ method public boolean isKeyDown(long key);
+ method public boolean isNumLockOn();
+ method public boolean isScrollLockOn();
+ method public void keyDown(long key);
+ method public void keyUp(long key);
+ property public abstract boolean isCapsLockOn;
+ property public abstract boolean isNumLockOn;
+ property public abstract boolean isScrollLockOn;
+ }
+
+ public final class KeyInjectionScopeKt {
+ method public static boolean isAltDown(androidx.compose.ui.test.KeyInjectionScope);
+ method public static boolean isCtrlDown(androidx.compose.ui.test.KeyInjectionScope);
+ method public static boolean isFnDown(androidx.compose.ui.test.KeyInjectionScope);
+ method public static boolean isMetaDown(androidx.compose.ui.test.KeyInjectionScope);
+ method public static boolean isShiftDown(androidx.compose.ui.test.KeyInjectionScope);
+ method public static void pressKey(androidx.compose.ui.test.KeyInjectionScope, long key, optional long pressDurationMillis);
+ method public static void withKeyDown(androidx.compose.ui.test.KeyInjectionScope, long key, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ method public static void withKeyToggled(androidx.compose.ui.test.KeyInjectionScope, long key, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ method public static void withKeysDown(androidx.compose.ui.test.KeyInjectionScope, java.util.List<androidx.compose.ui.input.key.Key> keys, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ method public static void withKeysToggled(androidx.compose.ui.test.KeyInjectionScope, java.util.List<androidx.compose.ui.input.key.Key> keys, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ }
+
+ public final class KeyInputHelpersKt {
+ method public static boolean performKeyPress(androidx.compose.ui.test.SemanticsNodeInteraction, android.view.KeyEvent keyEvent);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface MainTestClock {
+ method public void advanceTimeBy(long milliseconds, optional boolean ignoreFrameDuration);
+ method public void advanceTimeByFrame();
+ method public void advanceTimeUntil(optional long timeoutMillis, kotlin.jvm.functions.Function0<java.lang.Boolean> condition);
+ method public boolean getAutoAdvance();
+ method public long getCurrentTime();
+ method public void setAutoAdvance(boolean);
+ property public abstract boolean autoAdvance;
+ property public abstract long currentTime;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class MouseButton {
+ ctor public MouseButton(int buttonId);
+ method public int getButtonId();
+ property public final int buttonId;
+ field public static final androidx.compose.ui.test.MouseButton.Companion Companion;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public static final class MouseButton.Companion {
+ method public int getPrimary();
+ method public int getSecondary();
+ method public int getTertiary();
+ property public final int Primary;
+ property public final int Secondary;
+ property public final int Tertiary;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public interface MouseInjectionScope extends androidx.compose.ui.test.InjectionScope {
+ method public void cancel(optional long delayMillis);
+ method public void enter(optional long position, optional long delayMillis);
+ method public void exit(optional long position, optional long delayMillis);
+ method public long getCurrentPosition();
+ method public default void moveBy(long delta, optional long delayMillis);
+ method public void moveTo(long position, optional long delayMillis);
+ method public void press(optional int button);
+ method public void release(optional int button);
+ method public void scroll(float delta, optional int scrollWheel);
+ method public default void updatePointerBy(long delta);
+ method public void updatePointerTo(long position);
+ property public abstract long currentPosition;
+ }
+
+ public final class MouseInjectionScopeKt {
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void animateAlong(androidx.compose.ui.test.MouseInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, optional long durationMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void animateBy(androidx.compose.ui.test.MouseInjectionScope, long delta, optional long durationMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void animateTo(androidx.compose.ui.test.MouseInjectionScope, long position, optional long durationMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void click(androidx.compose.ui.test.MouseInjectionScope, optional long position);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void doubleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void dragAndDrop(androidx.compose.ui.test.MouseInjectionScope, long start, long end, optional long durationMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void longClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void rightClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void smoothScroll(androidx.compose.ui.test.MouseInjectionScope, float scrollAmount, optional long durationMillis, optional int scrollWheel);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void tripleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
+ }
+
+ public sealed interface MultiModalInjectionScope extends androidx.compose.ui.test.InjectionScope {
+ method @androidx.compose.ui.test.ExperimentalTestApi public void key(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void mouse(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void rotary(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
+ method public void touch(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.TouchInjectionScope,kotlin.Unit> block);
+ }
+
+ public final class OutputKt {
+ method public static void printToLog(androidx.compose.ui.test.SemanticsNodeInteraction, String tag, optional int maxDepth);
+ method public static void printToLog(androidx.compose.ui.test.SemanticsNodeInteractionCollection, String tag, optional int maxDepth);
+ method public static String printToString(androidx.compose.ui.test.SemanticsNodeInteraction, optional int maxDepth);
+ method public static String printToString(androidx.compose.ui.test.SemanticsNodeInteractionCollection, optional int maxDepth);
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public interface RotaryInjectionScope extends androidx.compose.ui.test.InjectionScope {
+ method public void rotateToScrollHorizontally(float horizontalScrollPixels);
+ method public void rotateToScrollVertically(float verticalScrollPixels);
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class ScrollWheel {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.ui.test.ScrollWheel.Companion Companion;
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi public static final class ScrollWheel.Companion {
+ method public int getHorizontal();
+ method public int getVertical();
+ property public final int Horizontal;
+ property public final int Vertical;
+ }
+
+ public final class SelectionResult {
+ ctor public SelectionResult(java.util.List<androidx.compose.ui.semantics.SemanticsNode> selectedNodes, optional String? customErrorOnNoMatch);
+ method public String? getCustomErrorOnNoMatch();
+ method public java.util.List<androidx.compose.ui.semantics.SemanticsNode> getSelectedNodes();
+ property public final String? customErrorOnNoMatch;
+ property public final java.util.List<androidx.compose.ui.semantics.SemanticsNode> selectedNodes;
+ }
+
+ public final class SelectorsKt {
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection filter(androidx.compose.ui.test.SemanticsNodeInteractionCollection, androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction filterToOne(androidx.compose.ui.test.SemanticsNodeInteractionCollection, androidx.compose.ui.test.SemanticsMatcher matcher);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onAncestors(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onChild(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onChildAt(androidx.compose.ui.test.SemanticsNodeInteraction, int index);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onChildren(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onFirst(androidx.compose.ui.test.SemanticsNodeInteractionCollection);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onLast(androidx.compose.ui.test.SemanticsNodeInteractionCollection);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onParent(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteraction onSibling(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static androidx.compose.ui.test.SemanticsNodeInteractionCollection onSiblings(androidx.compose.ui.test.SemanticsNodeInteraction);
+ }
+
+ public final class SemanticsMatcher {
+ ctor public SemanticsMatcher(String description, kotlin.jvm.functions.Function1<? super androidx.compose.ui.semantics.SemanticsNode,java.lang.Boolean> matcher);
+ method public infix androidx.compose.ui.test.SemanticsMatcher and(androidx.compose.ui.test.SemanticsMatcher other);
+ method public String getDescription();
+ method public boolean matches(androidx.compose.ui.semantics.SemanticsNode node);
+ method public boolean matchesAny(Iterable<androidx.compose.ui.semantics.SemanticsNode> nodes);
+ method public operator androidx.compose.ui.test.SemanticsMatcher not();
+ method public infix androidx.compose.ui.test.SemanticsMatcher or(androidx.compose.ui.test.SemanticsMatcher other);
+ property public final String description;
+ field public static final androidx.compose.ui.test.SemanticsMatcher.Companion Companion;
+ }
+
+ public static final class SemanticsMatcher.Companion {
+ method public <T> androidx.compose.ui.test.SemanticsMatcher expectValue(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T expectedValue);
+ method public <T> androidx.compose.ui.test.SemanticsMatcher keyIsDefined(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key);
+ method public <T> androidx.compose.ui.test.SemanticsMatcher keyNotDefined(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key);
+ }
+
+ public final class SemanticsNodeInteraction {
+ ctor public SemanticsNodeInteraction(androidx.compose.ui.test.TestContext testContext, boolean useUnmergedTree, androidx.compose.ui.test.SemanticsMatcher matcher);
+ ctor public SemanticsNodeInteraction(androidx.compose.ui.test.TestContext testContext, boolean useUnmergedTree, androidx.compose.ui.test.SemanticsSelector selector);
+ method public void assertDoesNotExist();
+ method public androidx.compose.ui.test.SemanticsNodeInteraction assertExists(optional String? errorMessageOnFail);
+ method public androidx.compose.ui.semantics.SemanticsNode fetchSemanticsNode(optional String? errorMessageOnFail);
+ }
+
+ public final class SemanticsNodeInteractionCollection {
+ ctor public SemanticsNodeInteractionCollection(androidx.compose.ui.test.TestContext testContext, boolean useUnmergedTree, androidx.compose.ui.test.SemanticsMatcher matcher);
+ ctor public SemanticsNodeInteractionCollection(androidx.compose.ui.test.TestContext testContext, boolean useUnmergedTree, androidx.compose.ui.test.SemanticsSelector selector);
+ method public java.util.List<androidx.compose.ui.semantics.SemanticsNode> fetchSemanticsNodes(optional boolean atLeastOneRootRequired, optional String? errorMessageOnFail);
+ method public operator androidx.compose.ui.test.SemanticsNodeInteraction get(int index);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface SemanticsNodeInteractionsProvider {
+ method public androidx.compose.ui.test.SemanticsNodeInteractionCollection onAllNodes(androidx.compose.ui.test.SemanticsMatcher matcher, optional boolean useUnmergedTree);
+ method public androidx.compose.ui.test.SemanticsNodeInteraction onNode(androidx.compose.ui.test.SemanticsMatcher matcher, optional boolean useUnmergedTree);
+ }
+
+ public final class SemanticsSelector {
+ ctor public SemanticsSelector(String description, boolean requiresExactlyOneNode, optional androidx.compose.ui.test.SemanticsSelector? chainedInputSelector, kotlin.jvm.functions.Function1<? super java.lang.Iterable<androidx.compose.ui.semantics.SemanticsNode>,androidx.compose.ui.test.SelectionResult> selector);
+ method public String getDescription();
+ method public androidx.compose.ui.test.SelectionResult map(Iterable<androidx.compose.ui.semantics.SemanticsNode> nodes, String errorOnFail);
+ property public final String description;
+ }
+
+ public final class TestContext {
+ }
+
+ @androidx.compose.ui.test.ExperimentalTestApi @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+ ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, optional long frameDelayNanos, optional kotlin.jvm.functions.Function1<? super java.lang.Long,kotlin.Unit> onPerformTraversals);
+ method @androidx.compose.ui.test.ExperimentalTestApi public kotlin.coroutines.ContinuationInterceptor getContinuationInterceptor();
+ method public long getFrameDelayNanos();
+ method public boolean getHasAwaiters();
+ method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ property @androidx.compose.ui.test.ExperimentalTestApi public final kotlin.coroutines.ContinuationInterceptor continuationInterceptor;
+ property public final long frameDelayNanos;
+ property public final boolean hasAwaiters;
+ }
+
+ public final class TestMonotonicFrameClock_jvmKt {
+ method @androidx.compose.ui.test.ExperimentalTestApi public static long getFrameDelayMillis(androidx.compose.ui.test.TestMonotonicFrameClock);
+ }
+
+ @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
+ method public androidx.compose.ui.test.MainTestClock getMainClock();
+ method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots(boolean atLeastOneRootExpected);
+ method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
+ property public abstract androidx.compose.ui.test.MainTestClock mainClock;
+ }
+
+ public final class TestOwnerKt {
+ method @androidx.compose.ui.test.InternalTestApi public static androidx.compose.ui.test.TestContext createTestContext(androidx.compose.ui.test.TestOwner owner);
+ }
+
+ public final class TextActionsKt {
+ method public static void performImeAction(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static void performTextClearance(androidx.compose.ui.test.SemanticsNodeInteraction);
+ method public static void performTextInput(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void performTextInputSelection(androidx.compose.ui.test.SemanticsNodeInteraction, long selection);
+ method public static void performTextReplacement(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface TouchInjectionScope extends androidx.compose.ui.test.InjectionScope {
+ method public void cancel(optional long delayMillis);
+ method public androidx.compose.ui.geometry.Offset? currentPosition(optional int pointerId);
+ method public void down(int pointerId, long position);
+ method public default void down(long position);
+ method public void move(optional long delayMillis);
+ method public default void moveBy(int pointerId, long delta, optional long delayMillis);
+ method public default void moveBy(long delta, optional long delayMillis);
+ method public default void moveTo(int pointerId, long position, optional long delayMillis);
+ method public default void moveTo(long position, optional long delayMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public default void moveWithHistory(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<androidx.compose.ui.geometry.Offset> historicalCoordinates, optional long delayMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public void moveWithHistoryMultiPointer(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<? extends java.util.List<androidx.compose.ui.geometry.Offset>> historicalCoordinates, optional long delayMillis);
+ method public void up(optional int pointerId);
+ method public default void updatePointerBy(int pointerId, long delta);
+ method public void updatePointerTo(int pointerId, long position);
+ }
+
+ public final class TouchInjectionScopeKt {
+ method public static void click(androidx.compose.ui.test.TouchInjectionScope, optional long position);
+ method public static void doubleClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long delayMillis);
+ method public static void longClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long durationMillis);
+ method @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method public static void pinch(androidx.compose.ui.test.TouchInjectionScope, long start0, long end0, long start1, long end1, optional long durationMillis);
+ method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, long start, long end, optional long durationMillis);
+ method public static void swipeDown(androidx.compose.ui.test.TouchInjectionScope, optional float startY, optional float endY, optional long durationMillis);
+ method public static void swipeLeft(androidx.compose.ui.test.TouchInjectionScope, optional float startX, optional float endX, optional long durationMillis);
+ method public static void swipeRight(androidx.compose.ui.test.TouchInjectionScope, optional float startX, optional float endX, optional long durationMillis);
+ method public static void swipeUp(androidx.compose.ui.test.TouchInjectionScope, optional float startY, optional float endY, optional long durationMillis);
+ method public static void swipeWithVelocity(androidx.compose.ui.test.TouchInjectionScope, long start, long end, float endVelocity, optional long durationMillis);
+ }
+
+}
+
+package androidx.compose.ui.test.internal {
+
+ @androidx.compose.ui.test.InternalTestApi public abstract class DelayPropagatingContinuationInterceptorWrapper extends kotlin.coroutines.AbstractCoroutineContextElement implements kotlin.coroutines.ContinuationInterceptor kotlinx.coroutines.Delay {
+ ctor public DelayPropagatingContinuationInterceptorWrapper(kotlin.coroutines.ContinuationInterceptor? wrappedInterceptor);
+ }
+
+}
+
diff --git a/compose/ui/ui-test/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-test/api/restricted_1.5.0-beta01.txt
index 73b5bac..f338e1a 100644
--- a/compose/ui/ui-test/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-test/api/restricted_1.5.0-beta01.txt
@@ -4,10 +4,7 @@
public final class ActionsKt {
method public static androidx.compose.ui.test.SemanticsNodeInteraction performClick(androidx.compose.ui.test.SemanticsNodeInteraction);
method @Deprecated public static androidx.compose.ui.test.SemanticsNodeInteraction performGesture(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.GestureScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performKeyInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performMouseInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performMultiModalInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MultiModalInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public static androidx.compose.ui.test.SemanticsNodeInteraction performRotaryScrollInput(androidx.compose.ui.test.SemanticsNodeInteraction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollTo(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToIndex(androidx.compose.ui.test.SemanticsNodeInteraction, int index);
method public static androidx.compose.ui.test.SemanticsNodeInteraction performScrollToKey(androidx.compose.ui.test.SemanticsNodeInteraction, Object key);
@@ -70,9 +67,6 @@
ctor public ComposeTimeoutException(String? message);
}
- @kotlin.RequiresOptIn(message="This testing API is experimental and is likely to be changed or removed entirely") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTestApi {
- }
-
public final class FiltersKt {
method public static androidx.compose.ui.test.SemanticsMatcher hasAnyAncestor(androidx.compose.ui.test.SemanticsMatcher matcher);
method public static androidx.compose.ui.test.SemanticsMatcher hasAnyChild(androidx.compose.ui.test.SemanticsMatcher matcher);
@@ -134,7 +128,6 @@
}
public final class GestureScopeKt {
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void advanceEventTime(androidx.compose.ui.test.GestureScope, long durationMillis);
method @Deprecated public static void cancel(androidx.compose.ui.test.GestureScope);
method @Deprecated public static void click(androidx.compose.ui.test.GestureScope, optional long position);
method @Deprecated public static void doubleClick(androidx.compose.ui.test.GestureScope, optional long position, optional long delayMillis);
@@ -169,13 +162,9 @@
method @Deprecated public static void pinch(androidx.compose.ui.test.GestureScope, long start0, long end0, long start1, long end1, optional long durationMillis);
method @Deprecated public static void swipe(androidx.compose.ui.test.GestureScope, long start, long end, optional long durationMillis);
method @Deprecated public static void swipeDown(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeDown(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
method @Deprecated public static void swipeLeft(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeLeft(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
method @Deprecated public static void swipeRight(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeRight(androidx.compose.ui.test.GestureScope, optional float startX, optional float endX, optional long durationMillis);
method @Deprecated public static void swipeUp(androidx.compose.ui.test.GestureScope);
- method @Deprecated @androidx.compose.ui.test.ExperimentalTestApi public static void swipeUp(androidx.compose.ui.test.GestureScope, optional float startY, optional float endY, optional long durationMillis);
method @Deprecated public static void swipeWithVelocity(androidx.compose.ui.test.GestureScope, long start, long end, float endVelocity, optional long durationMillis);
method @Deprecated public static void up(androidx.compose.ui.test.GestureScope, optional int pointerId);
}
@@ -231,9 +220,6 @@
property public default int width;
}
- @kotlin.RequiresOptIn(message="This is internal API for Compose modules that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalTestApi {
- }
-
@kotlin.jvm.JvmDefaultWithCompatibility public interface KeyInjectionScope extends androidx.compose.ui.test.InjectionScope {
method public boolean isCapsLockOn();
method public boolean isKeyDown(long key);
@@ -274,54 +260,7 @@
property public abstract long currentTime;
}
- @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class MouseButton {
- ctor public MouseButton(int buttonId);
- method public int getButtonId();
- property public final int buttonId;
- field public static final androidx.compose.ui.test.MouseButton.Companion Companion;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public static final class MouseButton.Companion {
- method public int getPrimary();
- method public int getSecondary();
- method public int getTertiary();
- property public final int Primary;
- property public final int Secondary;
- property public final int Tertiary;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public interface MouseInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method public void cancel(optional long delayMillis);
- method public void enter(optional long position, optional long delayMillis);
- method public void exit(optional long position, optional long delayMillis);
- method public long getCurrentPosition();
- method public default void moveBy(long delta, optional long delayMillis);
- method public void moveTo(long position, optional long delayMillis);
- method public void press(optional int button);
- method public void release(optional int button);
- method public void scroll(float delta, optional int scrollWheel);
- method public default void updatePointerBy(long delta);
- method public void updatePointerTo(long position);
- property public abstract long currentPosition;
- }
-
- public final class MouseInjectionScopeKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateAlong(androidx.compose.ui.test.MouseInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateBy(androidx.compose.ui.test.MouseInjectionScope, long delta, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void animateTo(androidx.compose.ui.test.MouseInjectionScope, long position, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void click(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void doubleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void dragAndDrop(androidx.compose.ui.test.MouseInjectionScope, long start, long end, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void longClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void rightClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void smoothScroll(androidx.compose.ui.test.MouseInjectionScope, float scrollAmount, optional long durationMillis, optional int scrollWheel);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void tripleClick(androidx.compose.ui.test.MouseInjectionScope, optional long position);
- }
-
public sealed interface MultiModalInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method @androidx.compose.ui.test.ExperimentalTestApi public void key(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.KeyInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public void mouse(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.MouseInjectionScope,kotlin.Unit> block);
- method @androidx.compose.ui.test.ExperimentalTestApi public void rotary(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.RotaryInjectionScope,kotlin.Unit> block);
method public void touch(kotlin.jvm.functions.Function1<? super androidx.compose.ui.test.TouchInjectionScope,kotlin.Unit> block);
}
@@ -332,24 +271,6 @@
method public static String printToString(androidx.compose.ui.test.SemanticsNodeInteractionCollection, optional int maxDepth);
}
- @androidx.compose.ui.test.ExperimentalTestApi public interface RotaryInjectionScope extends androidx.compose.ui.test.InjectionScope {
- method public void rotateToScrollHorizontally(float horizontalScrollPixels);
- method public void rotateToScrollVertically(float verticalScrollPixels);
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi @kotlin.jvm.JvmInline public final value class ScrollWheel {
- method public int getValue();
- property public final int value;
- field public static final androidx.compose.ui.test.ScrollWheel.Companion Companion;
- }
-
- @androidx.compose.ui.test.ExperimentalTestApi public static final class ScrollWheel.Companion {
- method public int getHorizontal();
- method public int getVertical();
- property public final int Horizontal;
- property public final int Vertical;
- }
-
public final class SelectionResult {
ctor public SelectionResult(java.util.List<androidx.compose.ui.semantics.SemanticsNode> selectedNodes, optional String? customErrorOnNoMatch);
method public String? getCustomErrorOnNoMatch();
@@ -420,37 +341,10 @@
public final class TestContext {
}
- @androidx.compose.ui.test.ExperimentalTestApi @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
- ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, optional long frameDelayNanos, optional kotlin.jvm.functions.Function1<? super java.lang.Long,kotlin.Unit> onPerformTraversals);
- method @androidx.compose.ui.test.ExperimentalTestApi public kotlin.coroutines.ContinuationInterceptor getContinuationInterceptor();
- method public long getFrameDelayNanos();
- method public boolean getHasAwaiters();
- method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
- property @androidx.compose.ui.test.ExperimentalTestApi public final kotlin.coroutines.ContinuationInterceptor continuationInterceptor;
- property public final long frameDelayNanos;
- property public final boolean hasAwaiters;
- }
-
- public final class TestMonotonicFrameClock_jvmKt {
- method @androidx.compose.ui.test.ExperimentalTestApi public static long getFrameDelayMillis(androidx.compose.ui.test.TestMonotonicFrameClock);
- }
-
- @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
- method public androidx.compose.ui.test.MainTestClock getMainClock();
- method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots(boolean atLeastOneRootExpected);
- method public <T> T runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
- property public abstract androidx.compose.ui.test.MainTestClock mainClock;
- }
-
- public final class TestOwnerKt {
- method @androidx.compose.ui.test.InternalTestApi public static androidx.compose.ui.test.TestContext createTestContext(androidx.compose.ui.test.TestOwner owner);
- }
-
public final class TextActionsKt {
method public static void performImeAction(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static void performTextClearance(androidx.compose.ui.test.SemanticsNodeInteraction);
method public static void performTextInput(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void performTextInputSelection(androidx.compose.ui.test.SemanticsNodeInteraction, long selection);
method public static void performTextReplacement(androidx.compose.ui.test.SemanticsNodeInteraction, String text);
}
@@ -464,8 +358,6 @@
method public default void moveBy(long delta, optional long delayMillis);
method public default void moveTo(int pointerId, long position, optional long delayMillis);
method public default void moveTo(long position, optional long delayMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public default void moveWithHistory(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<androidx.compose.ui.geometry.Offset> historicalCoordinates, optional long delayMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public void moveWithHistoryMultiPointer(java.util.List<java.lang.Long> relativeHistoricalTimes, java.util.List<? extends java.util.List<androidx.compose.ui.geometry.Offset>> historicalCoordinates, optional long delayMillis);
method public void up(optional int pointerId);
method public default void updatePointerBy(int pointerId, long delta);
method public void updatePointerTo(int pointerId, long position);
@@ -475,7 +367,6 @@
method public static void click(androidx.compose.ui.test.TouchInjectionScope, optional long position);
method public static void doubleClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long delayMillis);
method public static void longClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long durationMillis);
- method @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void pinch(androidx.compose.ui.test.TouchInjectionScope, long start0, long end0, long start1, long end1, optional long durationMillis);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, long start, long end, optional long durationMillis);
@@ -488,11 +379,3 @@
}
-package androidx.compose.ui.test.internal {
-
- @androidx.compose.ui.test.InternalTestApi public abstract class DelayPropagatingContinuationInterceptorWrapper extends kotlin.coroutines.AbstractCoroutineContextElement implements kotlin.coroutines.ContinuationInterceptor kotlinx.coroutines.Delay {
- ctor public DelayPropagatingContinuationInterceptorWrapper(kotlin.coroutines.ContinuationInterceptor? wrappedInterceptor);
- }
-
-}
-
diff --git a/compose/ui/ui-text-google-fonts/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-text-google-fonts/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..0321970
--- /dev/null
+++ b/compose/ui/ui-text-google-fonts/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.compose.ui.text.googlefonts {
+
+ public final class GoogleFont {
+ ctor public GoogleFont(String name, optional boolean bestEffort);
+ method public boolean getBestEffort();
+ method public String getName();
+ property public final boolean bestEffort;
+ property public final String name;
+ }
+
+ public static final class GoogleFont.Provider {
+ ctor public GoogleFont.Provider(String providerAuthority, String providerPackage, @ArrayRes int certificates);
+ ctor public GoogleFont.Provider(String providerAuthority, String providerPackage, java.util.List<? extends java.util.List<byte[]>> certificates);
+ }
+
+ public final class GoogleFontKt {
+ method public static androidx.compose.ui.text.font.Font Font(androidx.compose.ui.text.googlefonts.GoogleFont googleFont, androidx.compose.ui.text.googlefonts.GoogleFont.Provider fontProvider, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+ method @WorkerThread public static boolean isAvailableOnDevice(androidx.compose.ui.text.googlefonts.GoogleFont.Provider, android.content.Context context);
+ }
+
+}
+
diff --git a/compose/ui/ui-text/api/1.5.0-beta01.txt b/compose/ui/ui-text/api/1.5.0-beta01.txt
index b203bb5..4a40acd 100644
--- a/compose/ui/ui-text/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-text/api/1.5.0-beta01.txt
@@ -16,7 +16,6 @@
method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<java.lang.String>> getStringAnnotations(String tag, int start, int end);
method public String getText();
method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.TtsAnnotation>> getTtsAnnotations(int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.UrlAnnotation>> getUrlAnnotations(int start, int end);
method public boolean hasStringAnnotations(String tag, int start, int end);
method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.AnnotatedString plus(androidx.compose.ui.text.AnnotatedString other);
method public androidx.compose.ui.text.AnnotatedString subSequence(int startIndex, int endIndex);
@@ -34,8 +33,6 @@
method public void addStringAnnotation(String tag, String annotation, int start, int end);
method public void addStyle(androidx.compose.ui.text.ParagraphStyle style, int start, int end);
method public void addStyle(androidx.compose.ui.text.SpanStyle style, int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public void addTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation, int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public void addUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation, int start, int end);
method public void append(androidx.compose.ui.text.AnnotatedString text);
method public void append(androidx.compose.ui.text.AnnotatedString text, int start, int end);
method public androidx.compose.ui.text.AnnotatedString.Builder append(char char);
@@ -50,7 +47,6 @@
method public int pushStyle(androidx.compose.ui.text.ParagraphStyle style);
method public int pushStyle(androidx.compose.ui.text.SpanStyle style);
method public int pushTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation);
- method @androidx.compose.ui.text.ExperimentalTextApi public int pushUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
method public androidx.compose.ui.text.AnnotatedString toAnnotatedString();
property public final int length;
}
@@ -81,9 +77,6 @@
method public static androidx.compose.ui.text.AnnotatedString decapitalize(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
method public static androidx.compose.ui.text.AnnotatedString toLowerCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
method public static androidx.compose.ui.text.AnnotatedString toUpperCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.TtsAnnotation ttsAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.UrlAnnotation urlAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, String tag, String annotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.SpanStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
}
@@ -99,12 +92,6 @@
property public final int None;
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTextApi {
- }
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalTextApi {
- }
-
public final class MultiParagraph {
ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
@@ -616,12 +603,6 @@
public abstract sealed class TtsAnnotation {
}
- @androidx.compose.ui.text.ExperimentalTextApi public final class UrlAnnotation {
- ctor public UrlAnnotation(String url);
- method public String getUrl();
- property public final String url;
- }
-
public final class VerbatimTtsAnnotation extends androidx.compose.ui.text.TtsAnnotation {
ctor public VerbatimTtsAnnotation(String verbatim);
method public String getVerbatim();
@@ -630,13 +611,6 @@
}
-package androidx.compose.ui.text.android {
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalPlatformTextApi {
- }
-
-}
-
package androidx.compose.ui.text.font {
public abstract class AndroidFont implements androidx.compose.ui.text.font.Font {
@@ -742,7 +716,6 @@
public final class FontKt {
method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
- method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
}
@@ -885,15 +858,11 @@
public final class ResourceFont implements androidx.compose.ui.text.font.Font {
method public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
- method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
method public int getResId();
method public int getStyle();
- method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.FontVariation.Settings getVariationSettings();
method public androidx.compose.ui.text.font.FontWeight getWeight();
- property @androidx.compose.ui.text.ExperimentalTextApi public int loadingStrategy;
property public final int resId;
property public int style;
- property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.text.font.FontVariation.Settings variationSettings;
property public androidx.compose.ui.text.font.FontWeight weight;
}
@@ -1083,39 +1052,6 @@
property public final char mask;
}
- @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInput {
- method public void releaseInputFocus();
- method public void requestInputFocus();
- }
-
- @androidx.compose.ui.text.ExperimentalTextApi public interface PlatformTextInputAdapter {
- method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
- method public default void onDisposed();
- }
-
- @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
- method public T createAdapter(androidx.compose.ui.text.input.PlatformTextInput platformTextInput, android.view.View view);
- }
-
- @androidx.compose.runtime.Stable @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInputPluginRegistry {
- method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- }
-
- @androidx.compose.ui.text.InternalTextApi public final class PlatformTextInputPluginRegistryImpl implements androidx.compose.ui.text.input.PlatformTextInputPluginRegistry {
- ctor public PlatformTextInputPluginRegistryImpl(kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.PlatformTextInputPlugin<?>,? super androidx.compose.ui.text.input.PlatformTextInput,? extends androidx.compose.ui.text.input.PlatformTextInputAdapter> factory);
- method public androidx.compose.ui.text.input.PlatformTextInputAdapter? getFocusedAdapter();
- method @androidx.compose.ui.text.InternalTextApi public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> androidx.compose.ui.text.input.PlatformTextInputPluginRegistryImpl.AdapterHandle<T> getOrCreateAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- property public final androidx.compose.ui.text.input.PlatformTextInputAdapter? focusedAdapter;
- }
-
- @androidx.compose.ui.text.InternalTextApi public static final class PlatformTextInputPluginRegistryImpl.AdapterHandle<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
- ctor public PlatformTextInputPluginRegistryImpl.AdapterHandle(T adapter, kotlin.jvm.functions.Function0<java.lang.Boolean> onDispose);
- method public boolean dispose();
- method public T getAdapter();
- property public final T adapter;
- }
-
public interface PlatformTextInputService {
method public void hideSoftwareKeyboard();
method public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
@@ -1263,15 +1199,6 @@
}
-package androidx.compose.ui.text.platform {
-
- @androidx.compose.ui.text.InternalTextApi public final class URLSpanCache {
- ctor public URLSpanCache();
- method public android.text.style.URLSpan toURLSpan(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
- }
-
-}
-
package androidx.compose.ui.text.platform.extensions {
public final class TtsAnnotationExtensions_androidKt {
diff --git a/compose/ui/ui-text/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-text/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..b203bb5
--- /dev/null
+++ b/compose/ui/ui-text/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,1545 @@
+// Signature format: 4.0
+package androidx.compose.ui.text {
+
+ public final class AndroidTextStyle_androidKt {
+ method public static androidx.compose.ui.text.PlatformParagraphStyle lerp(androidx.compose.ui.text.PlatformParagraphStyle start, androidx.compose.ui.text.PlatformParagraphStyle stop, float fraction);
+ method public static androidx.compose.ui.text.PlatformSpanStyle lerp(androidx.compose.ui.text.PlatformSpanStyle start, androidx.compose.ui.text.PlatformSpanStyle stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class AnnotatedString implements java.lang.CharSequence {
+ ctor public AnnotatedString(String text, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.ParagraphStyle>> paragraphStyles);
+ method public operator char get(int index);
+ method public int getLength();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.ParagraphStyle>> getParagraphStyles();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> getSpanStyles();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<java.lang.String>> getStringAnnotations(int start, int end);
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<java.lang.String>> getStringAnnotations(String tag, int start, int end);
+ method public String getText();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.TtsAnnotation>> getTtsAnnotations(int start, int end);
+ method @androidx.compose.ui.text.ExperimentalTextApi public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.UrlAnnotation>> getUrlAnnotations(int start, int end);
+ method public boolean hasStringAnnotations(String tag, int start, int end);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.AnnotatedString plus(androidx.compose.ui.text.AnnotatedString other);
+ method public androidx.compose.ui.text.AnnotatedString subSequence(int startIndex, int endIndex);
+ method public androidx.compose.ui.text.AnnotatedString subSequence(long range);
+ property public int length;
+ property public final java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.ParagraphStyle>> paragraphStyles;
+ property public final java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles;
+ property public final String text;
+ }
+
+ public static final class AnnotatedString.Builder implements java.lang.Appendable {
+ ctor public AnnotatedString.Builder(androidx.compose.ui.text.AnnotatedString text);
+ ctor public AnnotatedString.Builder(optional int capacity);
+ ctor public AnnotatedString.Builder(String text);
+ method public void addStringAnnotation(String tag, String annotation, int start, int end);
+ method public void addStyle(androidx.compose.ui.text.ParagraphStyle style, int start, int end);
+ method public void addStyle(androidx.compose.ui.text.SpanStyle style, int start, int end);
+ method @androidx.compose.ui.text.ExperimentalTextApi public void addTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation, int start, int end);
+ method @androidx.compose.ui.text.ExperimentalTextApi public void addUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation, int start, int end);
+ method public void append(androidx.compose.ui.text.AnnotatedString text);
+ method public void append(androidx.compose.ui.text.AnnotatedString text, int start, int end);
+ method public androidx.compose.ui.text.AnnotatedString.Builder append(char char);
+ method @Deprecated public void append(char char);
+ method public androidx.compose.ui.text.AnnotatedString.Builder append(CharSequence? text);
+ method public androidx.compose.ui.text.AnnotatedString.Builder append(CharSequence? text, int start, int end);
+ method public void append(String text);
+ method public int getLength();
+ method public void pop();
+ method public void pop(int index);
+ method public int pushStringAnnotation(String tag, String annotation);
+ method public int pushStyle(androidx.compose.ui.text.ParagraphStyle style);
+ method public int pushStyle(androidx.compose.ui.text.SpanStyle style);
+ method public int pushTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation);
+ method @androidx.compose.ui.text.ExperimentalTextApi public int pushUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
+ method public androidx.compose.ui.text.AnnotatedString toAnnotatedString();
+ property public final int length;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class AnnotatedString.Range<T> {
+ ctor public AnnotatedString.Range(T item, int start, int end);
+ ctor public AnnotatedString.Range(T item, int start, int end, String tag);
+ method public T component1();
+ method public int component2();
+ method public int component3();
+ method public String component4();
+ method public androidx.compose.ui.text.AnnotatedString.Range<T> copy(T! item, int start, int end, String tag);
+ method public int getEnd();
+ method public T getItem();
+ method public int getStart();
+ method public String getTag();
+ property public final int end;
+ property public final T item;
+ property public final int start;
+ property public final String tag;
+ }
+
+ public final class AnnotatedStringKt {
+ method public static androidx.compose.ui.text.AnnotatedString AnnotatedString(String text, androidx.compose.ui.text.ParagraphStyle paragraphStyle);
+ method public static androidx.compose.ui.text.AnnotatedString AnnotatedString(String text, androidx.compose.ui.text.SpanStyle spanStyle, optional androidx.compose.ui.text.ParagraphStyle? paragraphStyle);
+ method public static inline androidx.compose.ui.text.AnnotatedString buildAnnotatedString(kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,kotlin.Unit> builder);
+ method public static androidx.compose.ui.text.AnnotatedString capitalize(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static androidx.compose.ui.text.AnnotatedString decapitalize(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static androidx.compose.ui.text.AnnotatedString toLowerCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static androidx.compose.ui.text.AnnotatedString toUpperCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
+ method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.TtsAnnotation ttsAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
+ method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.UrlAnnotation urlAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
+ method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, String tag, String annotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
+ method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
+ method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.SpanStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
+ }
+
+ @kotlin.jvm.JvmInline public final value class EmojiSupportMatch {
+ field public static final androidx.compose.ui.text.EmojiSupportMatch.Companion Companion;
+ }
+
+ public static final class EmojiSupportMatch.Companion {
+ method public int getDefault();
+ method public int getNone();
+ property public final int Default;
+ property public final int None;
+ }
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTextApi {
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalTextApi {
+ }
+
+ public final class MultiParagraph {
+ ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
+ ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
+ ctor public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, long constraints, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
+ ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.MultiParagraphIntrinsics intrinsics, optional int maxLines, optional boolean ellipsis, float width);
+ ctor public MultiParagraph(androidx.compose.ui.text.MultiParagraphIntrinsics intrinsics, long constraints, optional int maxLines, optional boolean ellipsis);
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getBidiRunDirection(int offset);
+ method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
+ method public androidx.compose.ui.geometry.Rect getCursorRect(int offset);
+ method public boolean getDidExceedMaxLines();
+ method public float getFirstBaseline();
+ method public float getHeight();
+ method public float getHorizontalPosition(int offset, boolean usePrimaryDirection);
+ method public androidx.compose.ui.text.MultiParagraphIntrinsics getIntrinsics();
+ method public float getLastBaseline();
+ method public float getLineBottom(int lineIndex);
+ method public int getLineCount();
+ method public int getLineEnd(int lineIndex, optional boolean visibleEnd);
+ method public int getLineForOffset(int offset);
+ method public int getLineForVerticalPosition(float vertical);
+ method public float getLineHeight(int lineIndex);
+ method public float getLineLeft(int lineIndex);
+ method public float getLineRight(int lineIndex);
+ method public int getLineStart(int lineIndex);
+ method public float getLineTop(int lineIndex);
+ method public float getLineWidth(int lineIndex);
+ method public float getMaxIntrinsicWidth();
+ method public int getMaxLines();
+ method public float getMinIntrinsicWidth();
+ method public int getOffsetForPosition(long position);
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getParagraphDirection(int offset);
+ method public androidx.compose.ui.graphics.Path getPathForRange(int start, int end);
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getPlaceholderRects();
+ method public float getWidth();
+ method public long getWordBoundary(int offset);
+ method public boolean isLineEllipsized(int lineIndex);
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+ method @Deprecated public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration);
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, optional long color, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? decoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+ property public final boolean didExceedMaxLines;
+ property public final float firstBaseline;
+ property public final float height;
+ property public final androidx.compose.ui.text.MultiParagraphIntrinsics intrinsics;
+ property public final float lastBaseline;
+ property public final int lineCount;
+ property public final float maxIntrinsicWidth;
+ property public final int maxLines;
+ property public final float minIntrinsicWidth;
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> placeholderRects;
+ property public final float width;
+ }
+
+ public final class MultiParagraphIntrinsics implements androidx.compose.ui.text.ParagraphIntrinsics {
+ ctor @Deprecated public MultiParagraphIntrinsics(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
+ ctor public MultiParagraphIntrinsics(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver);
+ method public androidx.compose.ui.text.AnnotatedString getAnnotatedString();
+ method public float getMaxIntrinsicWidth();
+ method public float getMinIntrinsicWidth();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> getPlaceholders();
+ property public final androidx.compose.ui.text.AnnotatedString annotatedString;
+ property public boolean hasStaleResolvedFonts;
+ property public float maxIntrinsicWidth;
+ property public float minIntrinsicWidth;
+ property public final java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders;
+ }
+
+ public sealed interface Paragraph {
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getBidiRunDirection(int offset);
+ method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
+ method public androidx.compose.ui.geometry.Rect getCursorRect(int offset);
+ method public boolean getDidExceedMaxLines();
+ method public float getFirstBaseline();
+ method public float getHeight();
+ method public float getHorizontalPosition(int offset, boolean usePrimaryDirection);
+ method public float getLastBaseline();
+ method public float getLineBottom(int lineIndex);
+ method public int getLineCount();
+ method public int getLineEnd(int lineIndex, boolean visibleEnd);
+ method public int getLineForOffset(int offset);
+ method public int getLineForVerticalPosition(float vertical);
+ method public float getLineHeight(int lineIndex);
+ method public float getLineLeft(int lineIndex);
+ method public float getLineRight(int lineIndex);
+ method public int getLineStart(int lineIndex);
+ method public float getLineTop(int lineIndex);
+ method public float getLineWidth(int lineIndex);
+ method public float getMaxIntrinsicWidth();
+ method public float getMinIntrinsicWidth();
+ method public int getOffsetForPosition(long position);
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getParagraphDirection(int offset);
+ method public androidx.compose.ui.graphics.Path getPathForRange(int start, int end);
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getPlaceholderRects();
+ method public float getWidth();
+ method public long getWordBoundary(int offset);
+ method public boolean isLineEllipsized(int lineIndex);
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.graphics.Brush brush, float alpha, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration);
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, long color, androidx.compose.ui.graphics.Shadow? shadow, androidx.compose.ui.text.style.TextDecoration? textDecoration, androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, int blendMode);
+ property public abstract boolean didExceedMaxLines;
+ property public abstract float firstBaseline;
+ property public abstract float height;
+ property public abstract float lastBaseline;
+ property public abstract int lineCount;
+ property public abstract float maxIntrinsicWidth;
+ property public abstract float minIntrinsicWidth;
+ property public abstract java.util.List<androidx.compose.ui.geometry.Rect> placeholderRects;
+ property public abstract float width;
+ }
+
+ public interface ParagraphIntrinsics {
+ method public default boolean getHasStaleResolvedFonts();
+ method public float getMaxIntrinsicWidth();
+ method public float getMinIntrinsicWidth();
+ property public default boolean hasStaleResolvedFonts;
+ property public abstract float maxIntrinsicWidth;
+ property public abstract float minIntrinsicWidth;
+ }
+
+ public final class ParagraphIntrinsicsKt {
+ method @Deprecated public static androidx.compose.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
+ method public static androidx.compose.ui.text.ParagraphIntrinsics ParagraphIntrinsics(String text, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver);
+ }
+
+ public final class ParagraphKt {
+ method @Deprecated public static androidx.compose.ui.text.Paragraph Paragraph(androidx.compose.ui.text.ParagraphIntrinsics paragraphIntrinsics, optional int maxLines, optional boolean ellipsis, float width);
+ method public static androidx.compose.ui.text.Paragraph Paragraph(androidx.compose.ui.text.ParagraphIntrinsics paragraphIntrinsics, long constraints, optional int maxLines, optional boolean ellipsis);
+ method @Deprecated public static androidx.compose.ui.text.Paragraph Paragraph(String text, androidx.compose.ui.text.TextStyle style, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
+ method @Deprecated public static androidx.compose.ui.text.Paragraph Paragraph(String text, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
+ method public static androidx.compose.ui.text.Paragraph Paragraph(String text, androidx.compose.ui.text.TextStyle style, long constraints, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>> spanStyles, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
+ }
+
+ @androidx.compose.runtime.Immutable public final class ParagraphStyle {
+ ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
+ ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+ ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+ ctor public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
+ method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+ method @Deprecated public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+ method public androidx.compose.ui.text.ParagraphStyle copy(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ method public androidx.compose.ui.text.style.Hyphens? getHyphens();
+ method public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+ method public long getLineHeight();
+ method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
+ method public androidx.compose.ui.text.PlatformParagraphStyle? getPlatformStyle();
+ method public androidx.compose.ui.text.style.TextAlign? getTextAlign();
+ method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+ method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle merge(optional androidx.compose.ui.text.ParagraphStyle? other);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.ParagraphStyle plus(androidx.compose.ui.text.ParagraphStyle other);
+ property public final androidx.compose.ui.text.style.Hyphens? hyphens;
+ property public final androidx.compose.ui.text.style.LineBreak? lineBreak;
+ property public final long lineHeight;
+ property public final androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle;
+ property public final androidx.compose.ui.text.PlatformParagraphStyle? platformStyle;
+ property public final androidx.compose.ui.text.style.TextAlign? textAlign;
+ property public final androidx.compose.ui.text.style.TextDirection? textDirection;
+ property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+ property public final androidx.compose.ui.text.style.TextMotion? textMotion;
+ }
+
+ public final class ParagraphStyleKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.ParagraphStyle lerp(androidx.compose.ui.text.ParagraphStyle start, androidx.compose.ui.text.ParagraphStyle stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class Placeholder {
+ ctor public Placeholder(long width, long height, int placeholderVerticalAlign);
+ method public androidx.compose.ui.text.Placeholder copy(optional long width, optional long height, optional int placeholderVerticalAlign);
+ method public long getHeight();
+ method public int getPlaceholderVerticalAlign();
+ method public long getWidth();
+ property public final long height;
+ property public final int placeholderVerticalAlign;
+ property public final long width;
+ }
+
+ @kotlin.jvm.JvmInline public final value class PlaceholderVerticalAlign {
+ field public static final androidx.compose.ui.text.PlaceholderVerticalAlign.Companion Companion;
+ }
+
+ public static final class PlaceholderVerticalAlign.Companion {
+ method public int getAboveBaseline();
+ method public int getBottom();
+ method public int getCenter();
+ method public int getTextBottom();
+ method public int getTextCenter();
+ method public int getTextTop();
+ method public int getTop();
+ property public final int AboveBaseline;
+ property public final int Bottom;
+ property public final int Center;
+ property public final int TextBottom;
+ property public final int TextCenter;
+ property public final int TextTop;
+ property public final int Top;
+ }
+
+ public final class PlatformParagraphStyle {
+ ctor public PlatformParagraphStyle(optional boolean includeFontPadding);
+ ctor public PlatformParagraphStyle(optional int emojiSupportMatch);
+ ctor public PlatformParagraphStyle(optional int emojiSupportMatch, optional boolean includeFontPadding);
+ method public int getEmojiSupportMatch();
+ method public boolean getIncludeFontPadding();
+ method public androidx.compose.ui.text.PlatformParagraphStyle merge(androidx.compose.ui.text.PlatformParagraphStyle? other);
+ property public final int emojiSupportMatch;
+ property public final boolean includeFontPadding;
+ field public static final androidx.compose.ui.text.PlatformParagraphStyle.Companion Companion;
+ }
+
+ public static final class PlatformParagraphStyle.Companion {
+ method public androidx.compose.ui.text.PlatformParagraphStyle getDefault();
+ property public final androidx.compose.ui.text.PlatformParagraphStyle Default;
+ }
+
+ public final class PlatformSpanStyle {
+ ctor public PlatformSpanStyle();
+ method public androidx.compose.ui.text.PlatformSpanStyle merge(androidx.compose.ui.text.PlatformSpanStyle? other);
+ field public static final androidx.compose.ui.text.PlatformSpanStyle.Companion Companion;
+ }
+
+ public static final class PlatformSpanStyle.Companion {
+ method public androidx.compose.ui.text.PlatformSpanStyle getDefault();
+ property public final androidx.compose.ui.text.PlatformSpanStyle Default;
+ }
+
+ public final class PlatformTextStyle {
+ ctor public PlatformTextStyle(androidx.compose.ui.text.PlatformSpanStyle? spanStyle, androidx.compose.ui.text.PlatformParagraphStyle? paragraphStyle);
+ ctor public PlatformTextStyle(optional boolean includeFontPadding);
+ ctor public PlatformTextStyle(int emojiSupportMatch);
+ method public androidx.compose.ui.text.PlatformParagraphStyle? getParagraphStyle();
+ method public androidx.compose.ui.text.PlatformSpanStyle? getSpanStyle();
+ property public final androidx.compose.ui.text.PlatformParagraphStyle? paragraphStyle;
+ property public final androidx.compose.ui.text.PlatformSpanStyle? spanStyle;
+ }
+
+ @androidx.compose.runtime.Immutable public final class SpanStyle {
+ ctor public SpanStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+ ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+ ctor @Deprecated public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+ ctor public SpanStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+ method public androidx.compose.ui.text.SpanStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+ method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow);
+ method @Deprecated public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle);
+ method public androidx.compose.ui.text.SpanStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.PlatformSpanStyle? platformStyle, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle);
+ method public float getAlpha();
+ method public long getBackground();
+ method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+ method public androidx.compose.ui.graphics.Brush? getBrush();
+ method public long getColor();
+ method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
+ method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
+ method public String? getFontFeatureSettings();
+ method public long getFontSize();
+ method public androidx.compose.ui.text.font.FontStyle? getFontStyle();
+ method public androidx.compose.ui.text.font.FontSynthesis? getFontSynthesis();
+ method public androidx.compose.ui.text.font.FontWeight? getFontWeight();
+ method public long getLetterSpacing();
+ method public androidx.compose.ui.text.intl.LocaleList? getLocaleList();
+ method public androidx.compose.ui.text.PlatformSpanStyle? getPlatformStyle();
+ method public androidx.compose.ui.graphics.Shadow? getShadow();
+ method public androidx.compose.ui.text.style.TextDecoration? getTextDecoration();
+ method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle merge(optional androidx.compose.ui.text.SpanStyle? other);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.SpanStyle plus(androidx.compose.ui.text.SpanStyle other);
+ property public final float alpha;
+ property public final long background;
+ property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+ property public final androidx.compose.ui.graphics.Brush? brush;
+ property public final long color;
+ property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
+ property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
+ property public final String? fontFeatureSettings;
+ property public final long fontSize;
+ property public final androidx.compose.ui.text.font.FontStyle? fontStyle;
+ property public final androidx.compose.ui.text.font.FontSynthesis? fontSynthesis;
+ property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
+ property public final long letterSpacing;
+ property public final androidx.compose.ui.text.intl.LocaleList? localeList;
+ property public final androidx.compose.ui.text.PlatformSpanStyle? platformStyle;
+ property public final androidx.compose.ui.graphics.Shadow? shadow;
+ property public final androidx.compose.ui.text.style.TextDecoration? textDecoration;
+ property public final androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform;
+ }
+
+ public final class SpanStyleKt {
+ method public static androidx.compose.ui.text.SpanStyle lerp(androidx.compose.ui.text.SpanStyle start, androidx.compose.ui.text.SpanStyle stop, float fraction);
+ }
+
+ public final class StringKt {
+ method public static String capitalize(String, androidx.compose.ui.text.intl.Locale locale);
+ method public static String capitalize(String, androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static String decapitalize(String, androidx.compose.ui.text.intl.Locale locale);
+ method public static String decapitalize(String, androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static String toLowerCase(String, androidx.compose.ui.text.intl.Locale locale);
+ method public static String toLowerCase(String, androidx.compose.ui.text.intl.LocaleList localeList);
+ method public static String toUpperCase(String, androidx.compose.ui.text.intl.Locale locale);
+ method public static String toUpperCase(String, androidx.compose.ui.text.intl.LocaleList localeList);
+ }
+
+ public final class TextLayoutInput {
+ ctor @Deprecated public TextLayoutInput(androidx.compose.ui.text.AnnotatedString text, androidx.compose.ui.text.TextStyle style, java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, int maxLines, boolean softWrap, int overflow, androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader, long constraints);
+ ctor public TextLayoutInput(androidx.compose.ui.text.AnnotatedString text, androidx.compose.ui.text.TextStyle style, java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, int maxLines, boolean softWrap, int overflow, androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, long constraints);
+ method @Deprecated public androidx.compose.ui.text.TextLayoutInput copy(optional androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean softWrap, optional int overflow, optional androidx.compose.ui.unit.Density density, optional androidx.compose.ui.unit.LayoutDirection layoutDirection, optional androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader, optional long constraints);
+ method public long getConstraints();
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.text.font.FontFamily.Resolver getFontFamilyResolver();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method public int getMaxLines();
+ method public int getOverflow();
+ method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> getPlaceholders();
+ method @Deprecated public androidx.compose.ui.text.font.Font.ResourceLoader getResourceLoader();
+ method public boolean getSoftWrap();
+ method public androidx.compose.ui.text.TextStyle getStyle();
+ method public androidx.compose.ui.text.AnnotatedString getText();
+ property public final long constraints;
+ property public final androidx.compose.ui.unit.Density density;
+ property public final androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver;
+ property public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ property public final int maxLines;
+ property public final int overflow;
+ property public final java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders;
+ property @Deprecated public final androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader;
+ property public final boolean softWrap;
+ property public final androidx.compose.ui.text.TextStyle style;
+ property public final androidx.compose.ui.text.AnnotatedString text;
+ }
+
+ public final class TextLayoutResult {
+ ctor public TextLayoutResult(androidx.compose.ui.text.TextLayoutInput layoutInput, androidx.compose.ui.text.MultiParagraph multiParagraph, long size);
+ method public androidx.compose.ui.text.TextLayoutResult copy(optional androidx.compose.ui.text.TextLayoutInput layoutInput, optional long size);
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getBidiRunDirection(int offset);
+ method public androidx.compose.ui.geometry.Rect getBoundingBox(int offset);
+ method public androidx.compose.ui.geometry.Rect getCursorRect(int offset);
+ method public boolean getDidOverflowHeight();
+ method public boolean getDidOverflowWidth();
+ method public float getFirstBaseline();
+ method public boolean getHasVisualOverflow();
+ method public float getHorizontalPosition(int offset, boolean usePrimaryDirection);
+ method public float getLastBaseline();
+ method public androidx.compose.ui.text.TextLayoutInput getLayoutInput();
+ method public float getLineBottom(int lineIndex);
+ method public int getLineCount();
+ method public int getLineEnd(int lineIndex, optional boolean visibleEnd);
+ method public int getLineForOffset(int offset);
+ method public int getLineForVerticalPosition(float vertical);
+ method public float getLineLeft(int lineIndex);
+ method public float getLineRight(int lineIndex);
+ method public int getLineStart(int lineIndex);
+ method public float getLineTop(int lineIndex);
+ method public androidx.compose.ui.text.MultiParagraph getMultiParagraph();
+ method public int getOffsetForPosition(long position);
+ method public androidx.compose.ui.text.style.ResolvedTextDirection getParagraphDirection(int offset);
+ method public androidx.compose.ui.graphics.Path getPathForRange(int start, int end);
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getPlaceholderRects();
+ method public long getSize();
+ method public long getWordBoundary(int offset);
+ method public boolean isLineEllipsized(int lineIndex);
+ property public final boolean didOverflowHeight;
+ property public final boolean didOverflowWidth;
+ property public final float firstBaseline;
+ property public final boolean hasVisualOverflow;
+ property public final float lastBaseline;
+ property public final androidx.compose.ui.text.TextLayoutInput layoutInput;
+ property public final int lineCount;
+ property public final androidx.compose.ui.text.MultiParagraph multiParagraph;
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> placeholderRects;
+ property public final long size;
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextMeasurer {
+ ctor public TextMeasurer(androidx.compose.ui.text.font.FontFamily.Resolver fallbackFontFamilyResolver, androidx.compose.ui.unit.Density fallbackDensity, androidx.compose.ui.unit.LayoutDirection fallbackLayoutDirection, optional int cacheSize);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextLayoutResult measure(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long constraints, optional androidx.compose.ui.unit.LayoutDirection layoutDirection, optional androidx.compose.ui.unit.Density density, optional androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional boolean skipCache);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextLayoutResult measure(String text, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long constraints, optional androidx.compose.ui.unit.LayoutDirection layoutDirection, optional androidx.compose.ui.unit.Density density, optional androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional boolean skipCache);
+ }
+
+ public final class TextPainter {
+ method public void paint(androidx.compose.ui.graphics.Canvas canvas, androidx.compose.ui.text.TextLayoutResult textLayoutResult);
+ field public static final androidx.compose.ui.text.TextPainter INSTANCE;
+ }
+
+ public final class TextPainterKt {
+ method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, androidx.compose.ui.graphics.Brush brush, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+ method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextLayoutResult textLayoutResult, optional long color, optional long topLeft, optional float alpha, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional int blendMode);
+ method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, androidx.compose.ui.text.AnnotatedString text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional long size, optional int blendMode);
+ method public static void drawText(androidx.compose.ui.graphics.drawscope.DrawScope, androidx.compose.ui.text.TextMeasurer textMeasurer, String text, optional long topLeft, optional androidx.compose.ui.text.TextStyle style, optional int overflow, optional boolean softWrap, optional int maxLines, optional long size, optional int blendMode);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TextRange {
+ method public operator boolean contains(int offset);
+ method public operator boolean contains(long other);
+ method public boolean getCollapsed();
+ method public int getEnd();
+ method public int getLength();
+ method public int getMax();
+ method public int getMin();
+ method public boolean getReversed();
+ method public int getStart();
+ method public boolean intersects(long other);
+ property public final boolean collapsed;
+ property public final int end;
+ property public final int length;
+ property public final int max;
+ property public final int min;
+ property public final boolean reversed;
+ property public final int start;
+ field public static final androidx.compose.ui.text.TextRange.Companion Companion;
+ }
+
+ public static final class TextRange.Companion {
+ method public long getZero();
+ property public final long Zero;
+ }
+
+ public final class TextRangeKt {
+ method public static long TextRange(int index);
+ method public static long TextRange(int start, int end);
+ method public static long coerceIn(long, int minimumValue, int maximumValue);
+ method public static String substring(CharSequence, long range);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextStyle {
+ ctor public TextStyle(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ ctor public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
+ ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+ ctor @Deprecated public TextStyle(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+ method public androidx.compose.ui.text.TextStyle copy(androidx.compose.ui.graphics.Brush? brush, optional float alpha, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ method public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
+ method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
+ method @Deprecated public androidx.compose.ui.text.TextStyle copy(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
+ method public float getAlpha();
+ method public long getBackground();
+ method public androidx.compose.ui.text.style.BaselineShift? getBaselineShift();
+ method public androidx.compose.ui.graphics.Brush? getBrush();
+ method public long getColor();
+ method public androidx.compose.ui.graphics.drawscope.DrawStyle? getDrawStyle();
+ method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
+ method public String? getFontFeatureSettings();
+ method public long getFontSize();
+ method public androidx.compose.ui.text.font.FontStyle? getFontStyle();
+ method public androidx.compose.ui.text.font.FontSynthesis? getFontSynthesis();
+ method public androidx.compose.ui.text.font.FontWeight? getFontWeight();
+ method public androidx.compose.ui.text.style.Hyphens? getHyphens();
+ method public long getLetterSpacing();
+ method public androidx.compose.ui.text.style.LineBreak? getLineBreak();
+ method public long getLineHeight();
+ method public androidx.compose.ui.text.style.LineHeightStyle? getLineHeightStyle();
+ method public androidx.compose.ui.text.intl.LocaleList? getLocaleList();
+ method public androidx.compose.ui.text.PlatformTextStyle? getPlatformStyle();
+ method public androidx.compose.ui.graphics.Shadow? getShadow();
+ method public androidx.compose.ui.text.style.TextAlign? getTextAlign();
+ method public androidx.compose.ui.text.style.TextDecoration? getTextDecoration();
+ method public androidx.compose.ui.text.style.TextDirection? getTextDirection();
+ method public androidx.compose.ui.text.style.TextGeometricTransform? getTextGeometricTransform();
+ method public androidx.compose.ui.text.style.TextIndent? getTextIndent();
+ method public androidx.compose.ui.text.style.TextMotion? getTextMotion();
+ method public boolean hasSameDrawAffectingAttributes(androidx.compose.ui.text.TextStyle other);
+ method public boolean hasSameLayoutAffectingAttributes(androidx.compose.ui.text.TextStyle other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.ParagraphStyle other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(androidx.compose.ui.text.SpanStyle other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional androidx.compose.ui.text.TextStyle? other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.TextStyle merge(optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional String? fontFeatureSettings, optional long letterSpacing, optional androidx.compose.ui.text.style.BaselineShift? baselineShift, optional androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform, optional androidx.compose.ui.text.intl.LocaleList? localeList, optional long background, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.graphics.Shadow? shadow, optional androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens, optional androidx.compose.ui.text.PlatformTextStyle? platformStyle, optional androidx.compose.ui.text.style.TextMotion? textMotion);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.ParagraphStyle other);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.SpanStyle other);
+ method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.TextStyle plus(androidx.compose.ui.text.TextStyle other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.ParagraphStyle toParagraphStyle();
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.text.SpanStyle toSpanStyle();
+ property public final float alpha;
+ property public final long background;
+ property public final androidx.compose.ui.text.style.BaselineShift? baselineShift;
+ property public final androidx.compose.ui.graphics.Brush? brush;
+ property public final long color;
+ property public final androidx.compose.ui.graphics.drawscope.DrawStyle? drawStyle;
+ property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
+ property public final String? fontFeatureSettings;
+ property public final long fontSize;
+ property public final androidx.compose.ui.text.font.FontStyle? fontStyle;
+ property public final androidx.compose.ui.text.font.FontSynthesis? fontSynthesis;
+ property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
+ property public final androidx.compose.ui.text.style.Hyphens? hyphens;
+ property public final long letterSpacing;
+ property public final androidx.compose.ui.text.style.LineBreak? lineBreak;
+ property public final long lineHeight;
+ property public final androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle;
+ property public final androidx.compose.ui.text.intl.LocaleList? localeList;
+ property public final androidx.compose.ui.text.PlatformTextStyle? platformStyle;
+ property public final androidx.compose.ui.graphics.Shadow? shadow;
+ property public final androidx.compose.ui.text.style.TextAlign? textAlign;
+ property public final androidx.compose.ui.text.style.TextDecoration? textDecoration;
+ property public final androidx.compose.ui.text.style.TextDirection? textDirection;
+ property public final androidx.compose.ui.text.style.TextGeometricTransform? textGeometricTransform;
+ property public final androidx.compose.ui.text.style.TextIndent? textIndent;
+ property public final androidx.compose.ui.text.style.TextMotion? textMotion;
+ field public static final androidx.compose.ui.text.TextStyle.Companion Companion;
+ }
+
+ public static final class TextStyle.Companion {
+ method public androidx.compose.ui.text.TextStyle getDefault();
+ property public final androidx.compose.ui.text.TextStyle Default;
+ }
+
+ public final class TextStyleKt {
+ method public static androidx.compose.ui.text.TextStyle lerp(androidx.compose.ui.text.TextStyle start, androidx.compose.ui.text.TextStyle stop, float fraction);
+ method public static androidx.compose.ui.text.TextStyle resolveDefaults(androidx.compose.ui.text.TextStyle style, androidx.compose.ui.unit.LayoutDirection direction);
+ }
+
+ public abstract sealed class TtsAnnotation {
+ }
+
+ @androidx.compose.ui.text.ExperimentalTextApi public final class UrlAnnotation {
+ ctor public UrlAnnotation(String url);
+ method public String getUrl();
+ property public final String url;
+ }
+
+ public final class VerbatimTtsAnnotation extends androidx.compose.ui.text.TtsAnnotation {
+ ctor public VerbatimTtsAnnotation(String verbatim);
+ method public String getVerbatim();
+ property public final String verbatim;
+ }
+
+}
+
+package androidx.compose.ui.text.android {
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalPlatformTextApi {
+ }
+
+}
+
+package androidx.compose.ui.text.font {
+
+ public abstract class AndroidFont implements androidx.compose.ui.text.font.Font {
+ ctor @Deprecated public AndroidFont(int loadingStrategy, androidx.compose.ui.text.font.AndroidFont.TypefaceLoader typefaceLoader);
+ ctor public AndroidFont(int loadingStrategy, androidx.compose.ui.text.font.AndroidFont.TypefaceLoader typefaceLoader, androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ method public final int getLoadingStrategy();
+ method public final androidx.compose.ui.text.font.AndroidFont.TypefaceLoader getTypefaceLoader();
+ method public final androidx.compose.ui.text.font.FontVariation.Settings getVariationSettings();
+ property public final int loadingStrategy;
+ property public final androidx.compose.ui.text.font.AndroidFont.TypefaceLoader typefaceLoader;
+ property public final androidx.compose.ui.text.font.FontVariation.Settings variationSettings;
+ }
+
+ public static interface AndroidFont.TypefaceLoader {
+ method public suspend Object? awaitLoad(android.content.Context context, androidx.compose.ui.text.font.AndroidFont font, kotlin.coroutines.Continuation<? super android.graphics.Typeface>);
+ method public android.graphics.Typeface? loadBlocking(android.content.Context context, androidx.compose.ui.text.font.AndroidFont font);
+ }
+
+ public final class AndroidFontKt {
+ method @RequiresApi(26) @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(android.os.ParcelFileDescriptor fileDescriptor, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(java.io.File file, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(String path, android.content.res.AssetManager assetManager, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ }
+
+ public final class AndroidTypeface_androidKt {
+ method public static androidx.compose.ui.text.font.FontFamily FontFamily(android.graphics.Typeface typeface);
+ method @Deprecated public static androidx.compose.ui.text.font.Typeface Typeface(android.content.Context context, androidx.compose.ui.text.font.FontFamily fontFamily, optional java.util.List<kotlin.Pair<androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontStyle>>? styles);
+ method public static androidx.compose.ui.text.font.Typeface Typeface(android.graphics.Typeface typeface);
+ }
+
+ public final class DelegatingFontLoaderForDeprecatedUsage_androidKt {
+ method @Deprecated public static androidx.compose.ui.text.font.FontFamily.Resolver createFontFamilyResolver(androidx.compose.ui.text.font.Font.ResourceLoader fontResourceLoader, android.content.Context context);
+ }
+
+ @kotlin.jvm.JvmInline public final value class DeviceFontFamilyName {
+ ctor public DeviceFontFamilyName(String name);
+ method public String getName();
+ property public final String name;
+ }
+
+ public final class DeviceFontFamilyNameFontKt {
+ method public static androidx.compose.ui.text.font.Font Font(String familyName, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ }
+
+ public abstract sealed class FileBasedFontFamily extends androidx.compose.ui.text.font.FontFamily {
+ }
+
+ @androidx.compose.runtime.Immutable public interface Font {
+ method public default int getLoadingStrategy();
+ method public int getStyle();
+ method public androidx.compose.ui.text.font.FontWeight getWeight();
+ property public default int loadingStrategy;
+ property public abstract int style;
+ property public abstract androidx.compose.ui.text.font.FontWeight weight;
+ field public static final androidx.compose.ui.text.font.Font.Companion Companion;
+ field public static final long MaximumAsyncTimeoutMillis = 15000L; // 0x3a98L
+ }
+
+ public static final class Font.Companion {
+ field public static final long MaximumAsyncTimeoutMillis = 15000L; // 0x3a98L
+ }
+
+ @Deprecated public static interface Font.ResourceLoader {
+ method @Deprecated public Object load(androidx.compose.ui.text.font.Font font);
+ }
+
+ @androidx.compose.runtime.Immutable public abstract sealed class FontFamily {
+ method @Deprecated public final boolean getCanLoadSynchronously();
+ property @Deprecated public final boolean canLoadSynchronously;
+ field public static final androidx.compose.ui.text.font.FontFamily.Companion Companion;
+ }
+
+ public static final class FontFamily.Companion {
+ method public androidx.compose.ui.text.font.GenericFontFamily getCursive();
+ method public androidx.compose.ui.text.font.SystemFontFamily getDefault();
+ method public androidx.compose.ui.text.font.GenericFontFamily getMonospace();
+ method public androidx.compose.ui.text.font.GenericFontFamily getSansSerif();
+ method public androidx.compose.ui.text.font.GenericFontFamily getSerif();
+ property public final androidx.compose.ui.text.font.GenericFontFamily Cursive;
+ property public final androidx.compose.ui.text.font.SystemFontFamily Default;
+ property public final androidx.compose.ui.text.font.GenericFontFamily Monospace;
+ property public final androidx.compose.ui.text.font.GenericFontFamily SansSerif;
+ property public final androidx.compose.ui.text.font.GenericFontFamily Serif;
+ }
+
+ public static sealed interface FontFamily.Resolver {
+ method public suspend Object? preload(androidx.compose.ui.text.font.FontFamily fontFamily, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.runtime.State<java.lang.Object> resolve(optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight fontWeight, optional int fontStyle, optional int fontSynthesis);
+ }
+
+ public final class FontFamilyKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily FontFamily(androidx.compose.ui.text.font.Font... fonts);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily FontFamily(androidx.compose.ui.text.font.Typeface typeface);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily FontFamily(java.util.List<? extends androidx.compose.ui.text.font.Font> fonts);
+ }
+
+ public final class FontFamilyResolver_androidKt {
+ method public static androidx.compose.ui.text.font.FontFamily.Resolver createFontFamilyResolver(android.content.Context context);
+ method public static androidx.compose.ui.text.font.FontFamily.Resolver createFontFamilyResolver(android.content.Context context, kotlin.coroutines.CoroutineContext coroutineContext);
+ method public static androidx.compose.runtime.State<android.graphics.Typeface> resolveAsTypeface(androidx.compose.ui.text.font.FontFamily.Resolver, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight fontWeight, optional int fontStyle, optional int fontSynthesis);
+ }
+
+ public final class FontKt {
+ method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
+ method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
+ }
+
+ @androidx.compose.runtime.Immutable public final class FontListFontFamily extends androidx.compose.ui.text.font.FileBasedFontFamily implements kotlin.jvm.internal.markers.KMappedMarker java.util.List<androidx.compose.ui.text.font.Font> {
+ method public java.util.List<androidx.compose.ui.text.font.Font> getFonts();
+ property public final java.util.List<androidx.compose.ui.text.font.Font> fonts;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontLoadingStrategy {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.ui.text.font.FontLoadingStrategy.Companion Companion;
+ }
+
+ public static final class FontLoadingStrategy.Companion {
+ method public int getAsync();
+ method public int getBlocking();
+ method public int getOptionalLocal();
+ property public final int Async;
+ property public final int Blocking;
+ property public final int OptionalLocal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontStyle {
+ ctor public FontStyle(int value);
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.ui.text.font.FontStyle.Companion Companion;
+ }
+
+ public static final class FontStyle.Companion {
+ method public int getItalic();
+ method public int getNormal();
+ method public java.util.List<androidx.compose.ui.text.font.FontStyle> values();
+ property public final int Italic;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontSynthesis {
+ field public static final androidx.compose.ui.text.font.FontSynthesis.Companion Companion;
+ }
+
+ public static final class FontSynthesis.Companion {
+ method public int getAll();
+ method public int getNone();
+ method public int getStyle();
+ method public int getWeight();
+ property public final int All;
+ property public final int None;
+ property public final int Style;
+ property public final int Weight;
+ }
+
+ public final class FontVariation {
+ method public androidx.compose.ui.text.font.FontVariation.Setting Setting(String name, float value);
+ method public androidx.compose.ui.text.font.FontVariation.Settings Settings(androidx.compose.ui.text.font.FontWeight weight, int style, androidx.compose.ui.text.font.FontVariation.Setting... settings);
+ method public androidx.compose.ui.text.font.FontVariation.Setting grade(int value);
+ method public androidx.compose.ui.text.font.FontVariation.Setting italic(float value);
+ method public androidx.compose.ui.text.font.FontVariation.Setting opticalSizing(long textSize);
+ method public androidx.compose.ui.text.font.FontVariation.Setting slant(float value);
+ method public androidx.compose.ui.text.font.FontVariation.Setting weight(int value);
+ method public androidx.compose.ui.text.font.FontVariation.Setting width(float value);
+ field public static final androidx.compose.ui.text.font.FontVariation INSTANCE;
+ }
+
+ @androidx.compose.runtime.Immutable public static sealed interface FontVariation.Setting {
+ method public String getAxisName();
+ method public boolean getNeedsDensity();
+ method public float toVariationValue(androidx.compose.ui.unit.Density? density);
+ property public abstract String axisName;
+ property public abstract boolean needsDensity;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class FontVariation.Settings {
+ ctor public FontVariation.Settings(androidx.compose.ui.text.font.FontVariation.Setting... settings);
+ method public java.util.List<androidx.compose.ui.text.font.FontVariation.Setting> getSettings();
+ property public final java.util.List<androidx.compose.ui.text.font.FontVariation.Setting> settings;
+ }
+
+ @androidx.compose.runtime.Immutable public final class FontWeight implements java.lang.Comparable<androidx.compose.ui.text.font.FontWeight> {
+ ctor public FontWeight(int weight);
+ method public operator int compareTo(androidx.compose.ui.text.font.FontWeight other);
+ method public int getWeight();
+ property public final int weight;
+ field public static final androidx.compose.ui.text.font.FontWeight.Companion Companion;
+ }
+
+ public static final class FontWeight.Companion {
+ method public androidx.compose.ui.text.font.FontWeight getBlack();
+ method public androidx.compose.ui.text.font.FontWeight getBold();
+ method public androidx.compose.ui.text.font.FontWeight getExtraBold();
+ method public androidx.compose.ui.text.font.FontWeight getExtraLight();
+ method public androidx.compose.ui.text.font.FontWeight getLight();
+ method public androidx.compose.ui.text.font.FontWeight getMedium();
+ method public androidx.compose.ui.text.font.FontWeight getNormal();
+ method public androidx.compose.ui.text.font.FontWeight getSemiBold();
+ method public androidx.compose.ui.text.font.FontWeight getThin();
+ method public androidx.compose.ui.text.font.FontWeight getW100();
+ method public androidx.compose.ui.text.font.FontWeight getW200();
+ method public androidx.compose.ui.text.font.FontWeight getW300();
+ method public androidx.compose.ui.text.font.FontWeight getW400();
+ method public androidx.compose.ui.text.font.FontWeight getW500();
+ method public androidx.compose.ui.text.font.FontWeight getW600();
+ method public androidx.compose.ui.text.font.FontWeight getW700();
+ method public androidx.compose.ui.text.font.FontWeight getW800();
+ method public androidx.compose.ui.text.font.FontWeight getW900();
+ property public final androidx.compose.ui.text.font.FontWeight Black;
+ property public final androidx.compose.ui.text.font.FontWeight Bold;
+ property public final androidx.compose.ui.text.font.FontWeight ExtraBold;
+ property public final androidx.compose.ui.text.font.FontWeight ExtraLight;
+ property public final androidx.compose.ui.text.font.FontWeight Light;
+ property public final androidx.compose.ui.text.font.FontWeight Medium;
+ property public final androidx.compose.ui.text.font.FontWeight Normal;
+ property public final androidx.compose.ui.text.font.FontWeight SemiBold;
+ property public final androidx.compose.ui.text.font.FontWeight Thin;
+ property public final androidx.compose.ui.text.font.FontWeight W100;
+ property public final androidx.compose.ui.text.font.FontWeight W200;
+ property public final androidx.compose.ui.text.font.FontWeight W300;
+ property public final androidx.compose.ui.text.font.FontWeight W400;
+ property public final androidx.compose.ui.text.font.FontWeight W500;
+ property public final androidx.compose.ui.text.font.FontWeight W600;
+ property public final androidx.compose.ui.text.font.FontWeight W700;
+ property public final androidx.compose.ui.text.font.FontWeight W800;
+ property public final androidx.compose.ui.text.font.FontWeight W900;
+ }
+
+ public final class FontWeightKt {
+ method public static androidx.compose.ui.text.font.FontWeight lerp(androidx.compose.ui.text.font.FontWeight start, androidx.compose.ui.text.font.FontWeight stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class GenericFontFamily extends androidx.compose.ui.text.font.SystemFontFamily {
+ method public String getName();
+ property public final String name;
+ }
+
+ public final class LoadedFontFamily extends androidx.compose.ui.text.font.FontFamily {
+ method public androidx.compose.ui.text.font.Typeface getTypeface();
+ property public final androidx.compose.ui.text.font.Typeface typeface;
+ }
+
+ public final class ResourceFont implements androidx.compose.ui.text.font.Font {
+ method public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
+ method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
+ method public int getResId();
+ method public int getStyle();
+ method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.FontVariation.Settings getVariationSettings();
+ method public androidx.compose.ui.text.font.FontWeight getWeight();
+ property @androidx.compose.ui.text.ExperimentalTextApi public int loadingStrategy;
+ property public final int resId;
+ property public int style;
+ property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.text.font.FontVariation.Settings variationSettings;
+ property public androidx.compose.ui.text.font.FontWeight weight;
+ }
+
+ public abstract sealed class SystemFontFamily extends androidx.compose.ui.text.font.FontFamily {
+ }
+
+ public interface Typeface {
+ method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
+ property public abstract androidx.compose.ui.text.font.FontFamily? fontFamily;
+ }
+
+}
+
+package androidx.compose.ui.text.input {
+
+ public final class BackspaceCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public BackspaceCommand();
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ }
+
+ public final class CommitTextCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public CommitTextCommand(androidx.compose.ui.text.AnnotatedString annotatedString, int newCursorPosition);
+ ctor public CommitTextCommand(String text, int newCursorPosition);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public androidx.compose.ui.text.AnnotatedString getAnnotatedString();
+ method public int getNewCursorPosition();
+ method public String getText();
+ property public final androidx.compose.ui.text.AnnotatedString annotatedString;
+ property public final int newCursorPosition;
+ property public final String text;
+ }
+
+ public final class DeleteAllCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public DeleteAllCommand();
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ }
+
+ public final class DeleteSurroundingTextCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public DeleteSurroundingTextCommand(int lengthBeforeCursor, int lengthAfterCursor);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public int getLengthAfterCursor();
+ method public int getLengthBeforeCursor();
+ property public final int lengthAfterCursor;
+ property public final int lengthBeforeCursor;
+ }
+
+ public final class DeleteSurroundingTextInCodePointsCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public DeleteSurroundingTextInCodePointsCommand(int lengthBeforeCursor, int lengthAfterCursor);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public int getLengthAfterCursor();
+ method public int getLengthBeforeCursor();
+ property public final int lengthAfterCursor;
+ property public final int lengthBeforeCursor;
+ }
+
+ public interface EditCommand {
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ }
+
+ public final class EditProcessor {
+ ctor public EditProcessor();
+ method public androidx.compose.ui.text.input.TextFieldValue apply(java.util.List<? extends androidx.compose.ui.text.input.EditCommand> editCommands);
+ method public void reset(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.TextInputSession? textInputSession);
+ method public androidx.compose.ui.text.input.TextFieldValue toTextFieldValue();
+ }
+
+ public final class EditingBuffer {
+ ctor public EditingBuffer(androidx.compose.ui.text.AnnotatedString text, long selection);
+ }
+
+ public final class FinishComposingTextCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public FinishComposingTextCommand();
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ }
+
+ @kotlin.jvm.JvmInline public final value class ImeAction {
+ field public static final androidx.compose.ui.text.input.ImeAction.Companion Companion;
+ }
+
+ public static final class ImeAction.Companion {
+ method public int getDefault();
+ method public int getDone();
+ method public int getGo();
+ method public int getNext();
+ method public int getNone();
+ method public int getPrevious();
+ method public int getSearch();
+ method public int getSend();
+ property public final int Default;
+ property public final int Done;
+ property public final int Go;
+ property public final int Next;
+ property public final int None;
+ property public final int Previous;
+ property public final int Search;
+ property public final int Send;
+ }
+
+ @androidx.compose.runtime.Immutable public final class ImeOptions {
+ ctor public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
+ method public androidx.compose.ui.text.input.ImeOptions copy(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
+ method public boolean getAutoCorrect();
+ method public int getCapitalization();
+ method public int getImeAction();
+ method public int getKeyboardType();
+ method public boolean getSingleLine();
+ property public final boolean autoCorrect;
+ property public final int capitalization;
+ property public final int imeAction;
+ property public final int keyboardType;
+ property public final boolean singleLine;
+ field public static final androidx.compose.ui.text.input.ImeOptions.Companion Companion;
+ }
+
+ public static final class ImeOptions.Companion {
+ method public androidx.compose.ui.text.input.ImeOptions getDefault();
+ property public final androidx.compose.ui.text.input.ImeOptions Default;
+ }
+
+ @Deprecated public interface InputEventCallback {
+ method @Deprecated public void onEditCommands(java.util.List<? extends androidx.compose.ui.text.input.EditCommand> editCommands);
+ method @Deprecated public void onImeAction(int imeAction);
+ }
+
+ @kotlin.jvm.JvmInline public final value class KeyboardCapitalization {
+ field public static final androidx.compose.ui.text.input.KeyboardCapitalization.Companion Companion;
+ }
+
+ public static final class KeyboardCapitalization.Companion {
+ method public int getCharacters();
+ method public int getNone();
+ method public int getSentences();
+ method public int getWords();
+ property public final int Characters;
+ property public final int None;
+ property public final int Sentences;
+ property public final int Words;
+ }
+
+ @kotlin.jvm.JvmInline public final value class KeyboardType {
+ field public static final androidx.compose.ui.text.input.KeyboardType.Companion Companion;
+ }
+
+ public static final class KeyboardType.Companion {
+ method public int getAscii();
+ method public int getDecimal();
+ method public int getEmail();
+ method public int getNumber();
+ method public int getNumberPassword();
+ method public int getPassword();
+ method public int getPhone();
+ method public int getText();
+ method public int getUri();
+ property public final int Ascii;
+ property public final int Decimal;
+ property public final int Email;
+ property public final int Number;
+ property public final int NumberPassword;
+ property public final int Password;
+ property public final int Phone;
+ property public final int Text;
+ property public final int Uri;
+ }
+
+ public final class MoveCursorCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public MoveCursorCommand(int amount);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public int getAmount();
+ property public final int amount;
+ }
+
+ public interface OffsetMapping {
+ method public int originalToTransformed(int offset);
+ method public int transformedToOriginal(int offset);
+ field public static final androidx.compose.ui.text.input.OffsetMapping.Companion Companion;
+ }
+
+ public static final class OffsetMapping.Companion {
+ method public androidx.compose.ui.text.input.OffsetMapping getIdentity();
+ property public final androidx.compose.ui.text.input.OffsetMapping Identity;
+ }
+
+ public final class PasswordVisualTransformation implements androidx.compose.ui.text.input.VisualTransformation {
+ ctor public PasswordVisualTransformation(optional char mask);
+ method public androidx.compose.ui.text.input.TransformedText filter(androidx.compose.ui.text.AnnotatedString text);
+ method public char getMask();
+ property public final char mask;
+ }
+
+ @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInput {
+ method public void releaseInputFocus();
+ method public void requestInputFocus();
+ }
+
+ @androidx.compose.ui.text.ExperimentalTextApi public interface PlatformTextInputAdapter {
+ method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
+ method public default void onDisposed();
+ }
+
+ @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
+ method public T createAdapter(androidx.compose.ui.text.input.PlatformTextInput platformTextInput, android.view.View view);
+ }
+
+ @androidx.compose.runtime.Stable @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInputPluginRegistry {
+ method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
+ }
+
+ @androidx.compose.ui.text.InternalTextApi public final class PlatformTextInputPluginRegistryImpl implements androidx.compose.ui.text.input.PlatformTextInputPluginRegistry {
+ ctor public PlatformTextInputPluginRegistryImpl(kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.PlatformTextInputPlugin<?>,? super androidx.compose.ui.text.input.PlatformTextInput,? extends androidx.compose.ui.text.input.PlatformTextInputAdapter> factory);
+ method public androidx.compose.ui.text.input.PlatformTextInputAdapter? getFocusedAdapter();
+ method @androidx.compose.ui.text.InternalTextApi public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> androidx.compose.ui.text.input.PlatformTextInputPluginRegistryImpl.AdapterHandle<T> getOrCreateAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
+ method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
+ property public final androidx.compose.ui.text.input.PlatformTextInputAdapter? focusedAdapter;
+ }
+
+ @androidx.compose.ui.text.InternalTextApi public static final class PlatformTextInputPluginRegistryImpl.AdapterHandle<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
+ ctor public PlatformTextInputPluginRegistryImpl.AdapterHandle(T adapter, kotlin.jvm.functions.Function0<java.lang.Boolean> onDispose);
+ method public boolean dispose();
+ method public T getAdapter();
+ property public final T adapter;
+ }
+
+ public interface PlatformTextInputService {
+ method public void hideSoftwareKeyboard();
+ method public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+ method public void showSoftwareKeyboard();
+ method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.ImeOptions imeOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditCommand>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+ method public void stopInput();
+ method public void updateState(androidx.compose.ui.text.input.TextFieldValue? oldValue, androidx.compose.ui.text.input.TextFieldValue newValue);
+ }
+
+ public final class SetComposingRegionCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public SetComposingRegionCommand(int start, int end);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public int getEnd();
+ method public int getStart();
+ property public final int end;
+ property public final int start;
+ }
+
+ public final class SetComposingTextCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public SetComposingTextCommand(androidx.compose.ui.text.AnnotatedString annotatedString, int newCursorPosition);
+ ctor public SetComposingTextCommand(String text, int newCursorPosition);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public androidx.compose.ui.text.AnnotatedString getAnnotatedString();
+ method public int getNewCursorPosition();
+ method public String getText();
+ property public final androidx.compose.ui.text.AnnotatedString annotatedString;
+ property public final int newCursorPosition;
+ property public final String text;
+ }
+
+ public final class SetSelectionCommand implements androidx.compose.ui.text.input.EditCommand {
+ ctor public SetSelectionCommand(int start, int end);
+ method public void applyTo(androidx.compose.ui.text.input.EditingBuffer buffer);
+ method public int getEnd();
+ method public int getStart();
+ property public final int end;
+ property public final int start;
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextFieldValue {
+ ctor public TextFieldValue(androidx.compose.ui.text.AnnotatedString annotatedString, optional long selection, optional androidx.compose.ui.text.TextRange? composition);
+ ctor public TextFieldValue(optional String text, optional long selection, optional androidx.compose.ui.text.TextRange? composition);
+ method public androidx.compose.ui.text.input.TextFieldValue copy(optional androidx.compose.ui.text.AnnotatedString annotatedString, optional long selection, optional androidx.compose.ui.text.TextRange? composition);
+ method public androidx.compose.ui.text.input.TextFieldValue copy(String text, optional long selection, optional androidx.compose.ui.text.TextRange? composition);
+ method public androidx.compose.ui.text.AnnotatedString getAnnotatedString();
+ method public androidx.compose.ui.text.TextRange? getComposition();
+ method public long getSelection();
+ method public String getText();
+ property public final androidx.compose.ui.text.AnnotatedString annotatedString;
+ property public final androidx.compose.ui.text.TextRange? composition;
+ property public final long selection;
+ property public final String text;
+ field public static final androidx.compose.ui.text.input.TextFieldValue.Companion Companion;
+ }
+
+ public static final class TextFieldValue.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.compose.ui.text.input.TextFieldValue,java.lang.Object> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.compose.ui.text.input.TextFieldValue,java.lang.Object> Saver;
+ }
+
+ public final class TextFieldValueKt {
+ method public static androidx.compose.ui.text.AnnotatedString getSelectedText(androidx.compose.ui.text.input.TextFieldValue);
+ method public static androidx.compose.ui.text.AnnotatedString getTextAfterSelection(androidx.compose.ui.text.input.TextFieldValue, int maxChars);
+ method public static androidx.compose.ui.text.AnnotatedString getTextBeforeSelection(androidx.compose.ui.text.input.TextFieldValue, int maxChars);
+ }
+
+ public class TextInputService {
+ ctor public TextInputService(androidx.compose.ui.text.input.PlatformTextInputService platformTextInputService);
+ method @Deprecated public final void hideSoftwareKeyboard();
+ method @Deprecated public final void showSoftwareKeyboard();
+ method public androidx.compose.ui.text.input.TextInputSession startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.ImeOptions imeOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditCommand>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+ method public void stopInput(androidx.compose.ui.text.input.TextInputSession session);
+ }
+
+ public final class TextInputSession {
+ ctor public TextInputSession(androidx.compose.ui.text.input.TextInputService textInputService, androidx.compose.ui.text.input.PlatformTextInputService platformTextInputService);
+ method public void dispose();
+ method public boolean hideSoftwareKeyboard();
+ method public boolean isOpen();
+ method public boolean notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
+ method public boolean showSoftwareKeyboard();
+ method public boolean updateState(androidx.compose.ui.text.input.TextFieldValue? oldValue, androidx.compose.ui.text.input.TextFieldValue newValue);
+ property public final boolean isOpen;
+ }
+
+ public final class TransformedText {
+ ctor public TransformedText(androidx.compose.ui.text.AnnotatedString text, androidx.compose.ui.text.input.OffsetMapping offsetMapping);
+ method public androidx.compose.ui.text.input.OffsetMapping getOffsetMapping();
+ method public androidx.compose.ui.text.AnnotatedString getText();
+ property public final androidx.compose.ui.text.input.OffsetMapping offsetMapping;
+ property public final androidx.compose.ui.text.AnnotatedString text;
+ }
+
+ @androidx.compose.runtime.Immutable public fun interface VisualTransformation {
+ method public androidx.compose.ui.text.input.TransformedText filter(androidx.compose.ui.text.AnnotatedString text);
+ field public static final androidx.compose.ui.text.input.VisualTransformation.Companion Companion;
+ }
+
+ public static final class VisualTransformation.Companion {
+ method public androidx.compose.ui.text.input.VisualTransformation getNone();
+ property public final androidx.compose.ui.text.input.VisualTransformation None;
+ }
+
+}
+
+package androidx.compose.ui.text.intl {
+
+ @androidx.compose.runtime.Immutable public final class Locale {
+ ctor public Locale(String languageTag);
+ method public String getLanguage();
+ method public String getRegion();
+ method public String getScript();
+ method public String toLanguageTag();
+ property public final String language;
+ property public final String region;
+ property public final String script;
+ field public static final androidx.compose.ui.text.intl.Locale.Companion Companion;
+ }
+
+ public static final class Locale.Companion {
+ method public androidx.compose.ui.text.intl.Locale getCurrent();
+ property public final androidx.compose.ui.text.intl.Locale current;
+ }
+
+ @androidx.compose.runtime.Immutable public final class LocaleList implements java.util.Collection<androidx.compose.ui.text.intl.Locale> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public LocaleList(androidx.compose.ui.text.intl.Locale... locales);
+ ctor public LocaleList(String languageTags);
+ ctor public LocaleList(java.util.List<androidx.compose.ui.text.intl.Locale> localeList);
+ method public operator boolean contains(androidx.compose.ui.text.intl.Locale element);
+ method public boolean containsAll(java.util.Collection<E!> elements);
+ method public operator androidx.compose.ui.text.intl.Locale get(int i);
+ method public java.util.List<androidx.compose.ui.text.intl.Locale> getLocaleList();
+ method public int getSize();
+ method public boolean isEmpty();
+ method public java.util.Iterator<androidx.compose.ui.text.intl.Locale> iterator();
+ property public final java.util.List<androidx.compose.ui.text.intl.Locale> localeList;
+ property public int size;
+ field public static final androidx.compose.ui.text.intl.LocaleList.Companion Companion;
+ }
+
+ public static final class LocaleList.Companion {
+ method public androidx.compose.ui.text.intl.LocaleList getCurrent();
+ property public final androidx.compose.ui.text.intl.LocaleList current;
+ }
+
+}
+
+package androidx.compose.ui.text.platform {
+
+ @androidx.compose.ui.text.InternalTextApi public final class URLSpanCache {
+ ctor public URLSpanCache();
+ method public android.text.style.URLSpan toURLSpan(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
+ }
+
+}
+
+package androidx.compose.ui.text.platform.extensions {
+
+ public final class TtsAnnotationExtensions_androidKt {
+ method public static android.text.style.TtsSpan toSpan(androidx.compose.ui.text.TtsAnnotation);
+ method public static android.text.style.TtsSpan toSpan(androidx.compose.ui.text.VerbatimTtsAnnotation);
+ }
+
+}
+
+package androidx.compose.ui.text.style {
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class BaselineShift {
+ ctor public BaselineShift(float multiplier);
+ method public float getMultiplier();
+ property public final float multiplier;
+ field public static final androidx.compose.ui.text.style.BaselineShift.Companion Companion;
+ }
+
+ public static final class BaselineShift.Companion {
+ method public float getNone();
+ method public float getSubscript();
+ method public float getSuperscript();
+ property public final float None;
+ property public final float Subscript;
+ property public final float Superscript;
+ }
+
+ public final class BaselineShiftKt {
+ method @androidx.compose.runtime.Stable public static float lerp(float start, float stop, float fraction);
+ }
+
+ @kotlin.jvm.JvmInline public final value class Hyphens {
+ field public static final androidx.compose.ui.text.style.Hyphens.Companion Companion;
+ }
+
+ public static final class Hyphens.Companion {
+ method public int getAuto();
+ method public int getNone();
+ property public final int Auto;
+ property public final int None;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class LineBreak {
+ ctor public LineBreak(int strategy, int strictness, int wordBreak);
+ method public int copy(optional int strategy, optional int strictness, optional int wordBreak);
+ method public int getStrategy();
+ method public int getStrictness();
+ method public int getWordBreak();
+ property public final int strategy;
+ property public final int strictness;
+ property public final int wordBreak;
+ field public static final androidx.compose.ui.text.style.LineBreak.Companion Companion;
+ }
+
+ public static final class LineBreak.Companion {
+ method public int getHeading();
+ method public int getParagraph();
+ method public int getSimple();
+ property public final int Heading;
+ property public final int Paragraph;
+ property public final int Simple;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class LineBreak.Strategy {
+ field public static final androidx.compose.ui.text.style.LineBreak.Strategy.Companion Companion;
+ }
+
+ public static final class LineBreak.Strategy.Companion {
+ method public int getBalanced();
+ method public int getHighQuality();
+ method public int getSimple();
+ property public final int Balanced;
+ property public final int HighQuality;
+ property public final int Simple;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class LineBreak.Strictness {
+ field public static final androidx.compose.ui.text.style.LineBreak.Strictness.Companion Companion;
+ }
+
+ public static final class LineBreak.Strictness.Companion {
+ method public int getDefault();
+ method public int getLoose();
+ method public int getNormal();
+ method public int getStrict();
+ property public final int Default;
+ property public final int Loose;
+ property public final int Normal;
+ property public final int Strict;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class LineBreak.WordBreak {
+ field public static final androidx.compose.ui.text.style.LineBreak.WordBreak.Companion Companion;
+ }
+
+ public static final class LineBreak.WordBreak.Companion {
+ method public int getDefault();
+ method public int getPhrase();
+ property public final int Default;
+ property public final int Phrase;
+ }
+
+ public final class LineHeightStyle {
+ ctor public LineHeightStyle(float alignment, int trim);
+ method public float getAlignment();
+ method public int getTrim();
+ property public final float alignment;
+ property public final int trim;
+ field public static final androidx.compose.ui.text.style.LineHeightStyle.Companion Companion;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class LineHeightStyle.Alignment {
+ ctor public LineHeightStyle.Alignment(float topRatio);
+ field public static final androidx.compose.ui.text.style.LineHeightStyle.Alignment.Companion Companion;
+ }
+
+ public static final class LineHeightStyle.Alignment.Companion {
+ method public float getBottom();
+ method public float getCenter();
+ method public float getProportional();
+ method public float getTop();
+ property public final float Bottom;
+ property public final float Center;
+ property public final float Proportional;
+ property public final float Top;
+ }
+
+ public static final class LineHeightStyle.Companion {
+ method public androidx.compose.ui.text.style.LineHeightStyle getDefault();
+ property public final androidx.compose.ui.text.style.LineHeightStyle Default;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class LineHeightStyle.Trim {
+ field public static final androidx.compose.ui.text.style.LineHeightStyle.Trim.Companion Companion;
+ }
+
+ public static final class LineHeightStyle.Trim.Companion {
+ method public int getBoth();
+ method public int getFirstLineTop();
+ method public int getLastLineBottom();
+ method public int getNone();
+ property public final int Both;
+ property public final int FirstLineTop;
+ property public final int LastLineBottom;
+ property public final int None;
+ }
+
+ public enum ResolvedTextDirection {
+ method public static androidx.compose.ui.text.style.ResolvedTextDirection valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.text.style.ResolvedTextDirection[] values();
+ enum_constant public static final androidx.compose.ui.text.style.ResolvedTextDirection Ltr;
+ enum_constant public static final androidx.compose.ui.text.style.ResolvedTextDirection Rtl;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextAlign {
+ field public static final androidx.compose.ui.text.style.TextAlign.Companion Companion;
+ }
+
+ public static final class TextAlign.Companion {
+ method public int getCenter();
+ method public int getEnd();
+ method public int getJustify();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public java.util.List<androidx.compose.ui.text.style.TextAlign> values();
+ property public final int Center;
+ property public final int End;
+ property public final int Justify;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextDecoration {
+ method public operator boolean contains(androidx.compose.ui.text.style.TextDecoration other);
+ method public int getMask();
+ method public operator androidx.compose.ui.text.style.TextDecoration plus(androidx.compose.ui.text.style.TextDecoration decoration);
+ property public final int mask;
+ field public static final androidx.compose.ui.text.style.TextDecoration.Companion Companion;
+ }
+
+ public static final class TextDecoration.Companion {
+ method public androidx.compose.ui.text.style.TextDecoration combine(java.util.List<androidx.compose.ui.text.style.TextDecoration> decorations);
+ method public androidx.compose.ui.text.style.TextDecoration getLineThrough();
+ method public androidx.compose.ui.text.style.TextDecoration getNone();
+ method public androidx.compose.ui.text.style.TextDecoration getUnderline();
+ property public final androidx.compose.ui.text.style.TextDecoration LineThrough;
+ property public final androidx.compose.ui.text.style.TextDecoration None;
+ property public final androidx.compose.ui.text.style.TextDecoration Underline;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextDirection {
+ field public static final androidx.compose.ui.text.style.TextDirection.Companion Companion;
+ }
+
+ public static final class TextDirection.Companion {
+ method public int getContent();
+ method public int getContentOrLtr();
+ method public int getContentOrRtl();
+ method public int getLtr();
+ method public int getRtl();
+ property public final int Content;
+ property public final int ContentOrLtr;
+ property public final int ContentOrRtl;
+ property public final int Ltr;
+ property public final int Rtl;
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextGeometricTransform {
+ ctor public TextGeometricTransform(optional float scaleX, optional float skewX);
+ method public androidx.compose.ui.text.style.TextGeometricTransform copy(optional float scaleX, optional float skewX);
+ method public float getScaleX();
+ method public float getSkewX();
+ property public final float scaleX;
+ property public final float skewX;
+ field public static final androidx.compose.ui.text.style.TextGeometricTransform.Companion Companion;
+ }
+
+ public static final class TextGeometricTransform.Companion {
+ }
+
+ public final class TextGeometricTransformKt {
+ method public static androidx.compose.ui.text.style.TextGeometricTransform lerp(androidx.compose.ui.text.style.TextGeometricTransform start, androidx.compose.ui.text.style.TextGeometricTransform stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextIndent {
+ ctor public TextIndent(optional long firstLine, optional long restLine);
+ method public androidx.compose.ui.text.style.TextIndent copy(optional long firstLine, optional long restLine);
+ method public long getFirstLine();
+ method public long getRestLine();
+ property public final long firstLine;
+ property public final long restLine;
+ field public static final androidx.compose.ui.text.style.TextIndent.Companion Companion;
+ }
+
+ public static final class TextIndent.Companion {
+ method public androidx.compose.ui.text.style.TextIndent getNone();
+ property public final androidx.compose.ui.text.style.TextIndent None;
+ }
+
+ public final class TextIndentKt {
+ method public static androidx.compose.ui.text.style.TextIndent lerp(androidx.compose.ui.text.style.TextIndent start, androidx.compose.ui.text.style.TextIndent stop, float fraction);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextMotion {
+ field public static final androidx.compose.ui.text.style.TextMotion.Companion Companion;
+ }
+
+ public static final class TextMotion.Companion {
+ method public androidx.compose.ui.text.style.TextMotion getAnimated();
+ method public androidx.compose.ui.text.style.TextMotion getStatic();
+ property public final androidx.compose.ui.text.style.TextMotion Animated;
+ property public final androidx.compose.ui.text.style.TextMotion Static;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextOverflow {
+ field public static final androidx.compose.ui.text.style.TextOverflow.Companion Companion;
+ }
+
+ public static final class TextOverflow.Companion {
+ method public int getClip();
+ method public int getEllipsis();
+ method public int getVisible();
+ property public final int Clip;
+ property public final int Ellipsis;
+ property public final int Visible;
+ }
+
+}
+
diff --git a/compose/ui/ui-text/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-text/api/restricted_1.5.0-beta01.txt
index b203bb5..4a40acd 100644
--- a/compose/ui/ui-text/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-text/api/restricted_1.5.0-beta01.txt
@@ -16,7 +16,6 @@
method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<java.lang.String>> getStringAnnotations(String tag, int start, int end);
method public String getText();
method public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.TtsAnnotation>> getTtsAnnotations(int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.UrlAnnotation>> getUrlAnnotations(int start, int end);
method public boolean hasStringAnnotations(String tag, int start, int end);
method @androidx.compose.runtime.Stable public operator androidx.compose.ui.text.AnnotatedString plus(androidx.compose.ui.text.AnnotatedString other);
method public androidx.compose.ui.text.AnnotatedString subSequence(int startIndex, int endIndex);
@@ -34,8 +33,6 @@
method public void addStringAnnotation(String tag, String annotation, int start, int end);
method public void addStyle(androidx.compose.ui.text.ParagraphStyle style, int start, int end);
method public void addStyle(androidx.compose.ui.text.SpanStyle style, int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public void addTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation, int start, int end);
- method @androidx.compose.ui.text.ExperimentalTextApi public void addUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation, int start, int end);
method public void append(androidx.compose.ui.text.AnnotatedString text);
method public void append(androidx.compose.ui.text.AnnotatedString text, int start, int end);
method public androidx.compose.ui.text.AnnotatedString.Builder append(char char);
@@ -50,7 +47,6 @@
method public int pushStyle(androidx.compose.ui.text.ParagraphStyle style);
method public int pushStyle(androidx.compose.ui.text.SpanStyle style);
method public int pushTtsAnnotation(androidx.compose.ui.text.TtsAnnotation ttsAnnotation);
- method @androidx.compose.ui.text.ExperimentalTextApi public int pushUrlAnnotation(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
method public androidx.compose.ui.text.AnnotatedString toAnnotatedString();
property public final int length;
}
@@ -81,9 +77,6 @@
method public static androidx.compose.ui.text.AnnotatedString decapitalize(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
method public static androidx.compose.ui.text.AnnotatedString toLowerCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
method public static androidx.compose.ui.text.AnnotatedString toUpperCase(androidx.compose.ui.text.AnnotatedString, optional androidx.compose.ui.text.intl.LocaleList localeList);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.TtsAnnotation ttsAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.UrlAnnotation urlAnnotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
- method @androidx.compose.ui.text.ExperimentalTextApi public static inline <R> R withAnnotation(androidx.compose.ui.text.AnnotatedString.Builder, String tag, String annotation, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.SpanStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
}
@@ -99,12 +92,6 @@
property public final int None;
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTextApi {
- }
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalTextApi {
- }
-
public final class MultiParagraph {
ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.FontFamily.Resolver fontFamilyResolver, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis);
ctor @Deprecated public MultiParagraph(androidx.compose.ui.text.AnnotatedString annotatedString, androidx.compose.ui.text.TextStyle style, optional java.util.List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.Placeholder>> placeholders, optional int maxLines, optional boolean ellipsis, float width, androidx.compose.ui.unit.Density density, androidx.compose.ui.text.font.Font.ResourceLoader resourceLoader);
@@ -616,12 +603,6 @@
public abstract sealed class TtsAnnotation {
}
- @androidx.compose.ui.text.ExperimentalTextApi public final class UrlAnnotation {
- ctor public UrlAnnotation(String url);
- method public String getUrl();
- property public final String url;
- }
-
public final class VerbatimTtsAnnotation extends androidx.compose.ui.text.TtsAnnotation {
ctor public VerbatimTtsAnnotation(String verbatim);
method public String getVerbatim();
@@ -630,13 +611,6 @@
}
-package androidx.compose.ui.text.android {
-
- @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalPlatformTextApi {
- }
-
-}
-
package androidx.compose.ui.text.font {
public abstract class AndroidFont implements androidx.compose.ui.text.font.Font {
@@ -742,7 +716,6 @@
public final class FontKt {
method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy);
- method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.ui.text.font.Font Font(int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
method @androidx.compose.runtime.Stable public static androidx.compose.ui.text.font.FontFamily toFontFamily(androidx.compose.ui.text.font.Font);
}
@@ -885,15 +858,11 @@
public final class ResourceFont implements androidx.compose.ui.text.font.Font {
method public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style);
- method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.ResourceFont copy(optional int resId, optional androidx.compose.ui.text.font.FontWeight weight, optional int style, optional int loadingStrategy, optional androidx.compose.ui.text.font.FontVariation.Settings variationSettings);
method public int getResId();
method public int getStyle();
- method @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.font.FontVariation.Settings getVariationSettings();
method public androidx.compose.ui.text.font.FontWeight getWeight();
- property @androidx.compose.ui.text.ExperimentalTextApi public int loadingStrategy;
property public final int resId;
property public int style;
- property @androidx.compose.ui.text.ExperimentalTextApi public final androidx.compose.ui.text.font.FontVariation.Settings variationSettings;
property public androidx.compose.ui.text.font.FontWeight weight;
}
@@ -1083,39 +1052,6 @@
property public final char mask;
}
- @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInput {
- method public void releaseInputFocus();
- method public void requestInputFocus();
- }
-
- @androidx.compose.ui.text.ExperimentalTextApi public interface PlatformTextInputAdapter {
- method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
- method public default void onDisposed();
- }
-
- @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
- method public T createAdapter(androidx.compose.ui.text.input.PlatformTextInput platformTextInput, android.view.View view);
- }
-
- @androidx.compose.runtime.Stable @androidx.compose.ui.text.ExperimentalTextApi public sealed interface PlatformTextInputPluginRegistry {
- method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- }
-
- @androidx.compose.ui.text.InternalTextApi public final class PlatformTextInputPluginRegistryImpl implements androidx.compose.ui.text.input.PlatformTextInputPluginRegistry {
- ctor public PlatformTextInputPluginRegistryImpl(kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.PlatformTextInputPlugin<?>,? super androidx.compose.ui.text.input.PlatformTextInput,? extends androidx.compose.ui.text.input.PlatformTextInputAdapter> factory);
- method public androidx.compose.ui.text.input.PlatformTextInputAdapter? getFocusedAdapter();
- method @androidx.compose.ui.text.InternalTextApi public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> androidx.compose.ui.text.input.PlatformTextInputPluginRegistryImpl.AdapterHandle<T> getOrCreateAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- method @androidx.compose.runtime.Composable public <T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> T rememberAdapter(androidx.compose.ui.text.input.PlatformTextInputPlugin<T> plugin);
- property public final androidx.compose.ui.text.input.PlatformTextInputAdapter? focusedAdapter;
- }
-
- @androidx.compose.ui.text.InternalTextApi public static final class PlatformTextInputPluginRegistryImpl.AdapterHandle<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
- ctor public PlatformTextInputPluginRegistryImpl.AdapterHandle(T adapter, kotlin.jvm.functions.Function0<java.lang.Boolean> onDispose);
- method public boolean dispose();
- method public T getAdapter();
- property public final T adapter;
- }
-
public interface PlatformTextInputService {
method public void hideSoftwareKeyboard();
method public default void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
@@ -1263,15 +1199,6 @@
}
-package androidx.compose.ui.text.platform {
-
- @androidx.compose.ui.text.InternalTextApi public final class URLSpanCache {
- ctor public URLSpanCache();
- method public android.text.style.URLSpan toURLSpan(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
- }
-
-}
-
package androidx.compose.ui.text.platform.extensions {
public final class TtsAnnotationExtensions_androidKt {
diff --git a/compose/ui/ui-text/benchmark/lint-baseline.xml b/compose/ui/ui-text/benchmark/lint-baseline.xml
index 94985f2..6431b9c 100644
--- a/compose/ui/ui-text/benchmark/lint-baseline.xml
+++ b/compose/ui/ui-text/benchmark/lint-baseline.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha08)" variant="all" version="7.4.0-alpha08">
+<issues format="6" by="lint 8.1.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta01)" variant="all" version="8.1.0-beta01">
<issue
id="SoonBlockedPrivateApi"
- message="Reflective access to freeTextLayoutCaches will throw an exception when targeting API 33 and above"
+ message="Reflective access to freeTextLayoutCaches will throw an exception when targeting API 34 and above"
errorLine1=" val freeCaches = Canvas::class.java.getDeclaredMethod("freeTextLayoutCaches")"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
diff --git a/compose/ui/ui-tooling-data/api/1.5.0-beta01.txt b/compose/ui/ui-tooling-data/api/1.5.0-beta01.txt
index 899fa9f..e6f50d0 100644
--- a/compose/ui/ui-tooling-data/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-tooling-data/api/1.5.0-beta01.txt
@@ -1,127 +1 @@
// Signature format: 4.0
-package androidx.compose.ui.tooling.data {
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class CallGroup extends androidx.compose.ui.tooling.data.Group {
- ctor public CallGroup(Object? key, String? name, androidx.compose.ui.unit.IntRect box, androidx.compose.ui.tooling.data.SourceLocation? location, Object? identity, java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children, boolean isInline);
- property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ContextCache {
- ctor public ContextCache();
- method public void clear();
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public abstract sealed class Group {
- method public final androidx.compose.ui.unit.IntRect getBox();
- method public final java.util.Collection<androidx.compose.ui.tooling.data.Group> getChildren();
- method public final java.util.Collection<java.lang.Object> getData();
- method public final Object? getIdentity();
- method public final Object? getKey();
- method public final androidx.compose.ui.tooling.data.SourceLocation? getLocation();
- method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
- method public final String? getName();
- method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
- method public final boolean isInline();
- property public final androidx.compose.ui.unit.IntRect box;
- property public final java.util.Collection<androidx.compose.ui.tooling.data.Group> children;
- property public final java.util.Collection<java.lang.Object> data;
- property public final Object? identity;
- property public final boolean isInline;
- property public final Object? key;
- property public final androidx.compose.ui.tooling.data.SourceLocation? location;
- property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
- property public final String? name;
- property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class JoinedKey {
- ctor public JoinedKey(Object? left, Object? right);
- method public Object? component1();
- method public Object? component2();
- method public androidx.compose.ui.tooling.data.JoinedKey copy(Object? left, Object? right);
- method public Object? getLeft();
- method public Object? getRight();
- property public final Object? left;
- property public final Object? right;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class NodeGroup extends androidx.compose.ui.tooling.data.Group {
- ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntRect box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children);
- method public Object getNode();
- property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
- property public final Object node;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ParameterInformation {
- ctor public ParameterInformation(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
- method public String component1();
- method public Object? component2();
- method public boolean component3();
- method public boolean component4();
- method public boolean component5();
- method public String? component6();
- method public boolean component7();
- method public androidx.compose.ui.tooling.data.ParameterInformation copy(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
- method public boolean getCompared();
- method public boolean getFromDefault();
- method public String? getInlineClass();
- method public String getName();
- method public boolean getStable();
- method public boolean getStatic();
- method public Object? getValue();
- property public final boolean compared;
- property public final boolean fromDefault;
- property public final String? inlineClass;
- property public final String name;
- property public final boolean stable;
- property public final boolean static;
- property public final Object? value;
- }
-
- public final class SlotTreeKt {
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static androidx.compose.ui.tooling.data.Group asTree(androidx.compose.runtime.tooling.CompositionData);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> findParameters(androidx.compose.runtime.tooling.CompositionGroup, optional androidx.compose.ui.tooling.data.ContextCache? cache);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static String? getPosition(androidx.compose.ui.tooling.data.Group);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static <T> T? mapTree(androidx.compose.runtime.tooling.CompositionData, kotlin.jvm.functions.Function3<? super androidx.compose.runtime.tooling.CompositionGroup,? super androidx.compose.ui.tooling.data.SourceContext,? super java.util.List<? extends T>,? extends T> factory, optional androidx.compose.ui.tooling.data.ContextCache cache);
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public interface SourceContext {
- method public androidx.compose.ui.unit.IntRect getBounds();
- method public int getDepth();
- method public androidx.compose.ui.tooling.data.SourceLocation? getLocation();
- method public String? getName();
- method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
- method public default boolean isInline();
- property public abstract androidx.compose.ui.unit.IntRect bounds;
- property public abstract int depth;
- property public default boolean isInline;
- property public abstract androidx.compose.ui.tooling.data.SourceLocation? location;
- property public abstract String? name;
- property public abstract java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class SourceLocation {
- ctor public SourceLocation(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
- method public int component1();
- method public int component2();
- method public int component3();
- method public String? component4();
- method public int component5();
- method public androidx.compose.ui.tooling.data.SourceLocation copy(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
- method public int getLength();
- method public int getLineNumber();
- method public int getOffset();
- method public int getPackageHash();
- method public String? getSourceFile();
- property public final int length;
- property public final int lineNumber;
- property public final int offset;
- property public final int packageHash;
- property public final String? sourceFile;
- }
-
- @kotlin.RequiresOptIn(message="This API is for tooling only and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface UiToolingDataApi {
- }
-
-}
-
diff --git a/compose/ui/ui-tooling-data/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-tooling-data/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..899fa9f
--- /dev/null
+++ b/compose/ui/ui-tooling-data/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,127 @@
+// Signature format: 4.0
+package androidx.compose.ui.tooling.data {
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class CallGroup extends androidx.compose.ui.tooling.data.Group {
+ ctor public CallGroup(Object? key, String? name, androidx.compose.ui.unit.IntRect box, androidx.compose.ui.tooling.data.SourceLocation? location, Object? identity, java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children, boolean isInline);
+ property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ContextCache {
+ ctor public ContextCache();
+ method public void clear();
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public abstract sealed class Group {
+ method public final androidx.compose.ui.unit.IntRect getBox();
+ method public final java.util.Collection<androidx.compose.ui.tooling.data.Group> getChildren();
+ method public final java.util.Collection<java.lang.Object> getData();
+ method public final Object? getIdentity();
+ method public final Object? getKey();
+ method public final androidx.compose.ui.tooling.data.SourceLocation? getLocation();
+ method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
+ method public final String? getName();
+ method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
+ method public final boolean isInline();
+ property public final androidx.compose.ui.unit.IntRect box;
+ property public final java.util.Collection<androidx.compose.ui.tooling.data.Group> children;
+ property public final java.util.Collection<java.lang.Object> data;
+ property public final Object? identity;
+ property public final boolean isInline;
+ property public final Object? key;
+ property public final androidx.compose.ui.tooling.data.SourceLocation? location;
+ property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
+ property public final String? name;
+ property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class JoinedKey {
+ ctor public JoinedKey(Object? left, Object? right);
+ method public Object? component1();
+ method public Object? component2();
+ method public androidx.compose.ui.tooling.data.JoinedKey copy(Object? left, Object? right);
+ method public Object? getLeft();
+ method public Object? getRight();
+ property public final Object? left;
+ property public final Object? right;
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class NodeGroup extends androidx.compose.ui.tooling.data.Group {
+ ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntRect box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children);
+ method public Object getNode();
+ property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
+ property public final Object node;
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ParameterInformation {
+ ctor public ParameterInformation(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
+ method public String component1();
+ method public Object? component2();
+ method public boolean component3();
+ method public boolean component4();
+ method public boolean component5();
+ method public String? component6();
+ method public boolean component7();
+ method public androidx.compose.ui.tooling.data.ParameterInformation copy(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
+ method public boolean getCompared();
+ method public boolean getFromDefault();
+ method public String? getInlineClass();
+ method public String getName();
+ method public boolean getStable();
+ method public boolean getStatic();
+ method public Object? getValue();
+ property public final boolean compared;
+ property public final boolean fromDefault;
+ property public final String? inlineClass;
+ property public final String name;
+ property public final boolean stable;
+ property public final boolean static;
+ property public final Object? value;
+ }
+
+ public final class SlotTreeKt {
+ method @androidx.compose.ui.tooling.data.UiToolingDataApi public static androidx.compose.ui.tooling.data.Group asTree(androidx.compose.runtime.tooling.CompositionData);
+ method @androidx.compose.ui.tooling.data.UiToolingDataApi public static java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> findParameters(androidx.compose.runtime.tooling.CompositionGroup, optional androidx.compose.ui.tooling.data.ContextCache? cache);
+ method @androidx.compose.ui.tooling.data.UiToolingDataApi public static String? getPosition(androidx.compose.ui.tooling.data.Group);
+ method @androidx.compose.ui.tooling.data.UiToolingDataApi public static <T> T? mapTree(androidx.compose.runtime.tooling.CompositionData, kotlin.jvm.functions.Function3<? super androidx.compose.runtime.tooling.CompositionGroup,? super androidx.compose.ui.tooling.data.SourceContext,? super java.util.List<? extends T>,? extends T> factory, optional androidx.compose.ui.tooling.data.ContextCache cache);
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public interface SourceContext {
+ method public androidx.compose.ui.unit.IntRect getBounds();
+ method public int getDepth();
+ method public androidx.compose.ui.tooling.data.SourceLocation? getLocation();
+ method public String? getName();
+ method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
+ method public default boolean isInline();
+ property public abstract androidx.compose.ui.unit.IntRect bounds;
+ property public abstract int depth;
+ property public default boolean isInline;
+ property public abstract androidx.compose.ui.tooling.data.SourceLocation? location;
+ property public abstract String? name;
+ property public abstract java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
+ }
+
+ @androidx.compose.ui.tooling.data.UiToolingDataApi public final class SourceLocation {
+ ctor public SourceLocation(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
+ method public int component1();
+ method public int component2();
+ method public int component3();
+ method public String? component4();
+ method public int component5();
+ method public androidx.compose.ui.tooling.data.SourceLocation copy(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
+ method public int getLength();
+ method public int getLineNumber();
+ method public int getOffset();
+ method public int getPackageHash();
+ method public String? getSourceFile();
+ property public final int length;
+ property public final int lineNumber;
+ property public final int offset;
+ property public final int packageHash;
+ property public final String? sourceFile;
+ }
+
+ @kotlin.RequiresOptIn(message="This API is for tooling only and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface UiToolingDataApi {
+ }
+
+}
+
diff --git a/compose/ui/ui-tooling-data/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-tooling-data/api/restricted_1.5.0-beta01.txt
index 899fa9f..e6f50d0 100644
--- a/compose/ui/ui-tooling-data/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-tooling-data/api/restricted_1.5.0-beta01.txt
@@ -1,127 +1 @@
// Signature format: 4.0
-package androidx.compose.ui.tooling.data {
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class CallGroup extends androidx.compose.ui.tooling.data.Group {
- ctor public CallGroup(Object? key, String? name, androidx.compose.ui.unit.IntRect box, androidx.compose.ui.tooling.data.SourceLocation? location, Object? identity, java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children, boolean isInline);
- property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ContextCache {
- ctor public ContextCache();
- method public void clear();
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public abstract sealed class Group {
- method public final androidx.compose.ui.unit.IntRect getBox();
- method public final java.util.Collection<androidx.compose.ui.tooling.data.Group> getChildren();
- method public final java.util.Collection<java.lang.Object> getData();
- method public final Object? getIdentity();
- method public final Object? getKey();
- method public final androidx.compose.ui.tooling.data.SourceLocation? getLocation();
- method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
- method public final String? getName();
- method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
- method public final boolean isInline();
- property public final androidx.compose.ui.unit.IntRect box;
- property public final java.util.Collection<androidx.compose.ui.tooling.data.Group> children;
- property public final java.util.Collection<java.lang.Object> data;
- property public final Object? identity;
- property public final boolean isInline;
- property public final Object? key;
- property public final androidx.compose.ui.tooling.data.SourceLocation? location;
- property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
- property public final String? name;
- property public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class JoinedKey {
- ctor public JoinedKey(Object? left, Object? right);
- method public Object? component1();
- method public Object? component2();
- method public androidx.compose.ui.tooling.data.JoinedKey copy(Object? left, Object? right);
- method public Object? getLeft();
- method public Object? getRight();
- property public final Object? left;
- property public final Object? right;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class NodeGroup extends androidx.compose.ui.tooling.data.Group {
- ctor public NodeGroup(Object? key, Object node, androidx.compose.ui.unit.IntRect box, java.util.Collection<?> data, java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.compose.ui.tooling.data.Group> children);
- method public Object getNode();
- property public java.util.List<androidx.compose.ui.layout.ModifierInfo> modifierInfo;
- property public final Object node;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class ParameterInformation {
- ctor public ParameterInformation(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
- method public String component1();
- method public Object? component2();
- method public boolean component3();
- method public boolean component4();
- method public boolean component5();
- method public String? component6();
- method public boolean component7();
- method public androidx.compose.ui.tooling.data.ParameterInformation copy(String name, Object? value, boolean fromDefault, boolean static, boolean compared, String? inlineClass, boolean stable);
- method public boolean getCompared();
- method public boolean getFromDefault();
- method public String? getInlineClass();
- method public String getName();
- method public boolean getStable();
- method public boolean getStatic();
- method public Object? getValue();
- property public final boolean compared;
- property public final boolean fromDefault;
- property public final String? inlineClass;
- property public final String name;
- property public final boolean stable;
- property public final boolean static;
- property public final Object? value;
- }
-
- public final class SlotTreeKt {
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static androidx.compose.ui.tooling.data.Group asTree(androidx.compose.runtime.tooling.CompositionData);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> findParameters(androidx.compose.runtime.tooling.CompositionGroup, optional androidx.compose.ui.tooling.data.ContextCache? cache);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static String? getPosition(androidx.compose.ui.tooling.data.Group);
- method @androidx.compose.ui.tooling.data.UiToolingDataApi public static <T> T? mapTree(androidx.compose.runtime.tooling.CompositionData, kotlin.jvm.functions.Function3<? super androidx.compose.runtime.tooling.CompositionGroup,? super androidx.compose.ui.tooling.data.SourceContext,? super java.util.List<? extends T>,? extends T> factory, optional androidx.compose.ui.tooling.data.ContextCache cache);
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public interface SourceContext {
- method public androidx.compose.ui.unit.IntRect getBounds();
- method public int getDepth();
- method public androidx.compose.ui.tooling.data.SourceLocation? getLocation();
- method public String? getName();
- method public java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> getParameters();
- method public default boolean isInline();
- property public abstract androidx.compose.ui.unit.IntRect bounds;
- property public abstract int depth;
- property public default boolean isInline;
- property public abstract androidx.compose.ui.tooling.data.SourceLocation? location;
- property public abstract String? name;
- property public abstract java.util.List<androidx.compose.ui.tooling.data.ParameterInformation> parameters;
- }
-
- @androidx.compose.ui.tooling.data.UiToolingDataApi public final class SourceLocation {
- ctor public SourceLocation(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
- method public int component1();
- method public int component2();
- method public int component3();
- method public String? component4();
- method public int component5();
- method public androidx.compose.ui.tooling.data.SourceLocation copy(int lineNumber, int offset, int length, String? sourceFile, int packageHash);
- method public int getLength();
- method public int getLineNumber();
- method public int getOffset();
- method public int getPackageHash();
- method public String? getSourceFile();
- property public final int length;
- property public final int lineNumber;
- property public final int offset;
- property public final int packageHash;
- property public final String? sourceFile;
- }
-
- @kotlin.RequiresOptIn(message="This API is for tooling only and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface UiToolingDataApi {
- }
-
-}
-
diff --git a/compose/ui/ui-tooling-preview/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-tooling-preview/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..ed0685d9
--- /dev/null
+++ b/compose/ui/ui-tooling-preview/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,113 @@
+// Signature format: 4.0
+package androidx.compose.ui.tooling.preview {
+
+ public final class Devices {
+ field public static final String AUTOMOTIVE_1024p = "id:automotive_1024p_landscape";
+ field public static final String DEFAULT = "";
+ field public static final String DESKTOP = "spec:id=reference_desktop,shape=Normal,width=1920,height=1080,unit=dp,dpi=160";
+ field public static final String FOLDABLE = "spec:id=reference_foldable,shape=Normal,width=673,height=841,unit=dp,dpi=420";
+ field public static final androidx.compose.ui.tooling.preview.Devices INSTANCE;
+ field public static final String NEXUS_10 = "name:Nexus 10";
+ field public static final String NEXUS_5 = "id:Nexus 5";
+ field public static final String NEXUS_5X = "id:Nexus 5X";
+ field public static final String NEXUS_6 = "id:Nexus 6";
+ field public static final String NEXUS_6P = "id:Nexus 6P";
+ field public static final String NEXUS_7 = "id:Nexus 7";
+ field public static final String NEXUS_7_2013 = "id:Nexus 7 2013";
+ field public static final String NEXUS_9 = "id:Nexus 9";
+ field public static final String PHONE = "spec:id=reference_phone,shape=Normal,width=411,height=891,unit=dp,dpi=420";
+ field public static final String PIXEL = "id:pixel";
+ field public static final String PIXEL_2 = "id:pixel_2";
+ field public static final String PIXEL_2_XL = "id:pixel_2_xl";
+ field public static final String PIXEL_3 = "id:pixel_3";
+ field public static final String PIXEL_3A = "id:pixel_3a";
+ field public static final String PIXEL_3A_XL = "id:pixel_3a_xl";
+ field public static final String PIXEL_3_XL = "id:pixel_3_xl";
+ field public static final String PIXEL_4 = "id:pixel_4";
+ field public static final String PIXEL_4_XL = "id:pixel_4_xl";
+ field public static final String PIXEL_C = "id:pixel_c";
+ field public static final String PIXEL_XL = "id:pixel_xl";
+ field public static final String TABLET = "spec:id=reference_tablet,shape=Normal,width=1280,height=800,unit=dp,dpi=240";
+ field public static final String TV_1080p = "spec:shape=Normal,width=1920,height=1080,unit=dp,dpi=420";
+ field public static final String TV_720p = "spec:shape=Normal,width=1280,height=720,unit=dp,dpi=420";
+ field public static final String WEAR_OS_LARGE_ROUND = "id:wearos_large_round";
+ field public static final String WEAR_OS_RECT = "id:wearos_rect";
+ field public static final String WEAR_OS_SMALL_ROUND = "id:wearos_small_round";
+ field public static final String WEAR_OS_SQUARE = "id:wearos_square";
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Preview {
+ method public abstract int apiLevel() default -1;
+ method public abstract long backgroundColor() default 0;
+ method public abstract String device() default androidx.compose.ui.tooling.preview.Devices.DEFAULT;
+ method public abstract float fontScale() default 1.0;
+ method public abstract String group() default "";
+ method public abstract int heightDp() default -1;
+ method public abstract String locale() default "";
+ method public abstract String name() default "";
+ method public abstract boolean showBackground() default false;
+ method public abstract boolean showSystemUi() default false;
+ method public abstract int uiMode() default 0;
+ method public abstract int wallpaper() default androidx.compose.ui.tooling.preview.Wallpapers.NONE;
+ method public abstract int widthDp() default -1;
+ property public abstract int apiLevel;
+ property public abstract long backgroundColor;
+ property public abstract String device;
+ property public abstract float fontScale;
+ property public abstract String group;
+ property public abstract int heightDp;
+ property public abstract String locale;
+ property public abstract String name;
+ property public abstract boolean showBackground;
+ property public abstract boolean showSystemUi;
+ property public abstract int uiMode;
+ property public abstract int wallpaper;
+ property public abstract int widthDp;
+ }
+
+ @kotlin.annotation.MustBeDocumented @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface Preview.Container {
+ method public abstract androidx.compose.ui.tooling.preview.Preview[] value();
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) public @interface PreviewParameter {
+ method public abstract int limit() default kotlin.jvm.internal.IntCompanionObject.MAX_VALUE;
+ method public abstract kotlin.reflect.KClass<? extends androidx.compose.ui.tooling.preview.PreviewParameterProvider<?>> provider();
+ property public abstract int limit;
+ property public abstract kotlin.reflect.KClass<? extends androidx.compose.ui.tooling.preview.PreviewParameterProvider<?>> provider;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface PreviewParameterProvider<T> {
+ method public default int getCount();
+ method public kotlin.sequences.Sequence<T> getValues();
+ property public default int count;
+ property public abstract kotlin.sequences.Sequence<T> values;
+ }
+
+ public final class Wallpapers {
+ field public static final int BLUE_DOMINATED_EXAMPLE = 2; // 0x2
+ field public static final int GREEN_DOMINATED_EXAMPLE = 1; // 0x1
+ field public static final androidx.compose.ui.tooling.preview.Wallpapers INSTANCE;
+ field public static final int NONE = -1; // 0xffffffff
+ field public static final int RED_DOMINATED_EXAMPLE = 0; // 0x0
+ field public static final int YELLOW_DOMINATED_EXAMPLE = 3; // 0x3
+ }
+
+}
+
+package androidx.compose.ui.tooling.preview.datasource {
+
+ public class CollectionPreviewParameterProvider<T> implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<T> {
+ ctor public CollectionPreviewParameterProvider(java.util.Collection<? extends T> collection);
+ method public kotlin.sequences.Sequence<T> getValues();
+ property public kotlin.sequences.Sequence<T> values;
+ }
+
+ public final class LoremIpsum implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<java.lang.String> {
+ ctor public LoremIpsum();
+ ctor public LoremIpsum(int words);
+ method public kotlin.sequences.Sequence<java.lang.String> getValues();
+ property public kotlin.sequences.Sequence<java.lang.String> values;
+ }
+
+}
+
diff --git a/compose/ui/ui-tooling/api/1.5.0-beta01.txt b/compose/ui/ui-tooling/api/1.5.0-beta01.txt
index df56c2d..486b41e 100644
--- a/compose/ui/ui-tooling/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-tooling/api/1.5.0-beta01.txt
@@ -1,11 +1,6 @@
// Signature format: 4.0
package androidx.compose.ui.tooling {
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class ComposableInvoker {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public void invokeComposable(String className, String methodName, androidx.compose.runtime.Composer composer, java.lang.Object?... args);
- field @Deprecated public static final androidx.compose.ui.tooling.ComposableInvoker INSTANCE;
- }
-
public final class InspectableKt {
method @Deprecated @androidx.compose.runtime.Composable public static void InInspectionModeOnly(kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
diff --git a/compose/ui/ui-tooling/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-tooling/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..df56c2d
--- /dev/null
+++ b/compose/ui/ui-tooling/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,25 @@
+// Signature format: 4.0
+package androidx.compose.ui.tooling {
+
+ @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class ComposableInvoker {
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public void invokeComposable(String className, String methodName, androidx.compose.runtime.Composer composer, java.lang.Object?... args);
+ field @Deprecated public static final androidx.compose.ui.tooling.ComposableInvoker INSTANCE;
+ }
+
+ public final class InspectableKt {
+ method @Deprecated @androidx.compose.runtime.Composable public static void InInspectionModeOnly(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+}
+
+package androidx.compose.ui.tooling.animation {
+
+ public final class ToolingState<T> implements androidx.compose.runtime.State<T> {
+ ctor public ToolingState(T default);
+ method public T getValue();
+ method public void setValue(T!);
+ property public T value;
+ }
+
+}
+
diff --git a/compose/ui/ui-tooling/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-tooling/api/restricted_1.5.0-beta01.txt
index df56c2d..486b41e 100644
--- a/compose/ui/ui-tooling/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-tooling/api/restricted_1.5.0-beta01.txt
@@ -1,11 +1,6 @@
// Signature format: 4.0
package androidx.compose.ui.tooling {
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class ComposableInvoker {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public void invokeComposable(String className, String methodName, androidx.compose.runtime.Composer composer, java.lang.Object?... args);
- field @Deprecated public static final androidx.compose.ui.tooling.ComposableInvoker INSTANCE;
- }
-
public final class InspectableKt {
method @Deprecated @androidx.compose.runtime.Composable public static void InInspectionModeOnly(kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
diff --git a/compose/ui/ui-unit/api/1.5.0-beta01.txt b/compose/ui/ui-unit/api/1.5.0-beta01.txt
index bc439ae..b58f3eb 100644
--- a/compose/ui/ui-unit/api/1.5.0-beta01.txt
+++ b/compose/ui/ui-unit/api/1.5.0-beta01.txt
@@ -194,9 +194,6 @@
property public final long Zero;
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalUnitApi {
- }
-
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntOffset {
method @androidx.compose.runtime.Stable public operator int component1();
method @androidx.compose.runtime.Stable public operator int component2();
diff --git a/compose/ui/ui-unit/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-unit/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..bc439ae
--- /dev/null
+++ b/compose/ui/ui-unit/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,417 @@
+// Signature format: 4.0
+package androidx.compose.ui.unit {
+
+ public final class AndroidDensity_androidKt {
+ method public static androidx.compose.ui.unit.Density Density(android.content.Context context);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Constraints {
+ ctor public Constraints(@kotlin.PublishedApi long value);
+ method public long copy(optional int minWidth, optional int maxWidth, optional int minHeight, optional int maxHeight);
+ method public boolean getHasBoundedHeight();
+ method public boolean getHasBoundedWidth();
+ method public boolean getHasFixedHeight();
+ method public boolean getHasFixedWidth();
+ method public int getMaxHeight();
+ method public int getMaxWidth();
+ method public int getMinHeight();
+ method public int getMinWidth();
+ method public boolean isZero();
+ property public final boolean hasBoundedHeight;
+ property public final boolean hasBoundedWidth;
+ property @androidx.compose.runtime.Stable public final boolean hasFixedHeight;
+ property @androidx.compose.runtime.Stable public final boolean hasFixedWidth;
+ property @androidx.compose.runtime.Stable public final boolean isZero;
+ property public final int maxHeight;
+ property public final int maxWidth;
+ property public final int minHeight;
+ property public final int minWidth;
+ field public static final androidx.compose.ui.unit.Constraints.Companion Companion;
+ field public static final int Infinity = 2147483647; // 0x7fffffff
+ }
+
+ public static final class Constraints.Companion {
+ method @androidx.compose.runtime.Stable public long fixed(int width, int height);
+ method @androidx.compose.runtime.Stable public long fixedHeight(int height);
+ method @androidx.compose.runtime.Stable public long fixedWidth(int width);
+ }
+
+ public final class ConstraintsKt {
+ method @androidx.compose.runtime.Stable public static long Constraints(optional int minWidth, optional int maxWidth, optional int minHeight, optional int maxHeight);
+ method public static long constrain(long, long otherConstraints);
+ method @androidx.compose.runtime.Stable public static long constrain(long, long size);
+ method @androidx.compose.runtime.Stable public static int constrainHeight(long, int height);
+ method @androidx.compose.runtime.Stable public static int constrainWidth(long, int width);
+ method @androidx.compose.runtime.Stable public static boolean isSatisfiedBy(long, long size);
+ method @androidx.compose.runtime.Stable public static long offset(long, optional int horizontal, optional int vertical);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface Density {
+ method public float getDensity();
+ method public float getFontScale();
+ method @androidx.compose.runtime.Stable public default int roundToPx(float);
+ method @androidx.compose.runtime.Stable public default int roundToPx(long);
+ method @androidx.compose.runtime.Stable public default float toDp(float);
+ method @androidx.compose.runtime.Stable public default float toDp(int);
+ method @androidx.compose.runtime.Stable public default float toDp(long);
+ method @androidx.compose.runtime.Stable public default long toDpSize(long);
+ method @androidx.compose.runtime.Stable public default float toPx(float);
+ method @androidx.compose.runtime.Stable public default float toPx(long);
+ method @androidx.compose.runtime.Stable public default androidx.compose.ui.geometry.Rect toRect(androidx.compose.ui.unit.DpRect);
+ method @androidx.compose.runtime.Stable public default long toSize(long);
+ method @androidx.compose.runtime.Stable public default long toSp(float);
+ method @androidx.compose.runtime.Stable public default long toSp(float);
+ method @androidx.compose.runtime.Stable public default long toSp(int);
+ property public abstract float density;
+ property public abstract float fontScale;
+ }
+
+ public final class DensityKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.Density Density(float density, optional float fontScale);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Dp implements java.lang.Comparable<androidx.compose.ui.unit.Dp> {
+ ctor public Dp(float value);
+ method @androidx.compose.runtime.Stable public operator int compareTo(float other);
+ method @androidx.compose.runtime.Stable public inline operator float div(float other);
+ method @androidx.compose.runtime.Stable public inline operator float div(float other);
+ method @androidx.compose.runtime.Stable public inline operator float div(int other);
+ method public float getValue();
+ method @androidx.compose.runtime.Stable public inline operator float minus(float other);
+ method @androidx.compose.runtime.Stable public inline operator float plus(float other);
+ method @androidx.compose.runtime.Stable public inline operator float times(float other);
+ method @androidx.compose.runtime.Stable public inline operator float times(int other);
+ method @androidx.compose.runtime.Stable public inline operator float unaryMinus();
+ property public final float value;
+ field public static final androidx.compose.ui.unit.Dp.Companion Companion;
+ }
+
+ public static final class Dp.Companion {
+ method public float getHairline();
+ method public float getInfinity();
+ method public float getUnspecified();
+ property public final float Hairline;
+ property public final float Infinity;
+ property public final float Unspecified;
+ }
+
+ public final class DpKt {
+ method @androidx.compose.runtime.Stable public static long DpOffset(float x, float y);
+ method @androidx.compose.runtime.Stable public static long DpSize(float width, float height);
+ method @androidx.compose.runtime.Stable public static inline float coerceAtLeast(float, float minimumValue);
+ method @androidx.compose.runtime.Stable public static inline float coerceAtMost(float, float maximumValue);
+ method @androidx.compose.runtime.Stable public static inline float coerceIn(float, float minimumValue, float maximumValue);
+ method public static long getCenter(long);
+ method public static inline float getDp(double);
+ method public static inline float getDp(float);
+ method public static inline float getDp(int);
+ method public static inline float getHeight(androidx.compose.ui.unit.DpRect);
+ method public static inline long getSize(androidx.compose.ui.unit.DpRect);
+ method public static inline float getWidth(androidx.compose.ui.unit.DpRect);
+ method public static inline boolean isFinite(float);
+ method public static inline boolean isSpecified(float);
+ method public static inline boolean isSpecified(long);
+ method public static inline boolean isSpecified(long);
+ method public static inline boolean isUnspecified(float);
+ method public static inline boolean isUnspecified(long);
+ method public static inline boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static float lerp(float start, float stop, float fraction);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static inline float max(float a, float b);
+ method @androidx.compose.runtime.Stable public static inline float min(float a, float b);
+ method public static inline float takeOrElse(float, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.Dp> block);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.DpOffset> block);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.DpSize> block);
+ method @androidx.compose.runtime.Stable public static inline operator float times(double, float other);
+ method @androidx.compose.runtime.Stable public static inline operator float times(float, float other);
+ method @androidx.compose.runtime.Stable public static inline operator long times(float, long size);
+ method @androidx.compose.runtime.Stable public static inline operator float times(int, float other);
+ method @androidx.compose.runtime.Stable public static inline operator long times(int, long size);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DpOffset {
+ method public long copy(optional float x, optional float y);
+ method public float getX();
+ method public float getY();
+ method @androidx.compose.runtime.Stable public inline operator long minus(long other);
+ method @androidx.compose.runtime.Stable public inline operator long plus(long other);
+ property @androidx.compose.runtime.Stable public final float x;
+ property @androidx.compose.runtime.Stable public final float y;
+ field public static final androidx.compose.ui.unit.DpOffset.Companion Companion;
+ }
+
+ public static final class DpOffset.Companion {
+ method public long getUnspecified();
+ method public long getZero();
+ property public final long Unspecified;
+ property public final long Zero;
+ }
+
+ @androidx.compose.runtime.Immutable public final class DpRect {
+ ctor public DpRect(@androidx.compose.runtime.Stable float left, @androidx.compose.runtime.Stable float top, @androidx.compose.runtime.Stable float right, @androidx.compose.runtime.Stable float bottom);
+ ctor public DpRect(long origin, long size);
+ method public float component1-D9Ej5fM();
+ method public float component2-D9Ej5fM();
+ method public float component3-D9Ej5fM();
+ method public float component4-D9Ej5fM();
+ method public androidx.compose.ui.unit.DpRect copy-a9UjIt4(float left, float top, float right, float bottom);
+ method public float getBottom();
+ method public float getLeft();
+ method public float getRight();
+ method public float getTop();
+ property public final float bottom;
+ property public final float left;
+ property public final float right;
+ property public final float top;
+ field public static final androidx.compose.ui.unit.DpRect.Companion Companion;
+ }
+
+ public static final class DpRect.Companion {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DpSize {
+ method @androidx.compose.runtime.Stable public inline operator float component1();
+ method @androidx.compose.runtime.Stable public inline operator float component2();
+ method public long copy(optional float width, optional float height);
+ method @androidx.compose.runtime.Stable public operator long div(float other);
+ method @androidx.compose.runtime.Stable public operator long div(int other);
+ method public float getHeight();
+ method public float getWidth();
+ method @androidx.compose.runtime.Stable public inline operator long minus(long other);
+ method @androidx.compose.runtime.Stable public inline operator long plus(long other);
+ method @androidx.compose.runtime.Stable public operator long times(float other);
+ method @androidx.compose.runtime.Stable public operator long times(int other);
+ property @androidx.compose.runtime.Stable public final float height;
+ property @androidx.compose.runtime.Stable public final float width;
+ field public static final androidx.compose.ui.unit.DpSize.Companion Companion;
+ }
+
+ public static final class DpSize.Companion {
+ method public long getUnspecified();
+ method public long getZero();
+ property public final long Unspecified;
+ property public final long Zero;
+ }
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalUnitApi {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntOffset {
+ method @androidx.compose.runtime.Stable public operator int component1();
+ method @androidx.compose.runtime.Stable public operator int component2();
+ method public long copy(optional int x, optional int y);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public int getX();
+ method public int getY();
+ method @androidx.compose.runtime.Stable public inline operator long minus(long other);
+ method @androidx.compose.runtime.Stable public inline operator long plus(long other);
+ method @androidx.compose.runtime.Stable public operator long rem(int operand);
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ method @androidx.compose.runtime.Stable public inline operator long unaryMinus();
+ property @androidx.compose.runtime.Stable public final int x;
+ property @androidx.compose.runtime.Stable public final int y;
+ field public static final androidx.compose.ui.unit.IntOffset.Companion Companion;
+ }
+
+ public static final class IntOffset.Companion {
+ method public long getZero();
+ property public final long Zero;
+ }
+
+ public final class IntOffsetKt {
+ method @androidx.compose.runtime.Stable public static long IntOffset(int x, int y);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
+ method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
+ method @androidx.compose.runtime.Stable public static operator long plus(long, long offset);
+ method @androidx.compose.runtime.Stable public static operator long plus(long, long offset);
+ method @androidx.compose.runtime.Stable public static inline long round(long);
+ method @androidx.compose.runtime.Stable public static inline long toOffset(long);
+ }
+
+ @androidx.compose.runtime.Immutable public final class IntRect {
+ ctor public IntRect(@androidx.compose.runtime.Stable int left, @androidx.compose.runtime.Stable int top, @androidx.compose.runtime.Stable int right, @androidx.compose.runtime.Stable int bottom);
+ method public int component1();
+ method public int component2();
+ method public int component3();
+ method public int component4();
+ method public boolean contains(long offset);
+ method public androidx.compose.ui.unit.IntRect copy(int left, int top, int right, int bottom);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.unit.IntRect deflate(int delta);
+ method public int getBottom();
+ method public long getBottomCenter();
+ method public long getBottomLeft();
+ method public long getBottomRight();
+ method public long getCenter();
+ method public long getCenterLeft();
+ method public long getCenterRight();
+ method public int getHeight();
+ method public int getLeft();
+ method public int getMaxDimension();
+ method public int getMinDimension();
+ method public int getRight();
+ method public long getSize();
+ method public int getTop();
+ method public long getTopCenter();
+ method public long getTopLeft();
+ method public long getTopRight();
+ method public int getWidth();
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.unit.IntRect inflate(int delta);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.unit.IntRect intersect(androidx.compose.ui.unit.IntRect other);
+ method public boolean isEmpty();
+ method public boolean overlaps(androidx.compose.ui.unit.IntRect other);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.unit.IntRect translate(int translateX, int translateY);
+ method @androidx.compose.runtime.Stable public androidx.compose.ui.unit.IntRect translate(long offset);
+ property public final int bottom;
+ property public final long bottomCenter;
+ property public final long bottomLeft;
+ property public final long bottomRight;
+ property public final long center;
+ property public final long centerLeft;
+ property public final long centerRight;
+ property @androidx.compose.runtime.Stable public final int height;
+ property @androidx.compose.runtime.Stable public final boolean isEmpty;
+ property public final int left;
+ property public final int maxDimension;
+ property public final int minDimension;
+ property public final int right;
+ property @androidx.compose.runtime.Stable public final long size;
+ property public final int top;
+ property public final long topCenter;
+ property public final long topLeft;
+ property public final long topRight;
+ property @androidx.compose.runtime.Stable public final int width;
+ field public static final androidx.compose.ui.unit.IntRect.Companion Companion;
+ }
+
+ public static final class IntRect.Companion {
+ method public androidx.compose.ui.unit.IntRect getZero();
+ property public final androidx.compose.ui.unit.IntRect Zero;
+ }
+
+ public final class IntRectKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect IntRect(long center, int radius);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect IntRect(long offset, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect IntRect(long topLeft, long bottomRight);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect lerp(androidx.compose.ui.unit.IntRect start, androidx.compose.ui.unit.IntRect stop, float fraction);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect roundToIntRect(androidx.compose.ui.geometry.Rect);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.geometry.Rect toRect(androidx.compose.ui.unit.IntRect);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntSize {
+ method @androidx.compose.runtime.Stable public inline operator int component1();
+ method @androidx.compose.runtime.Stable public inline operator int component2();
+ method @androidx.compose.runtime.Stable public operator long div(int other);
+ method public int getHeight();
+ method public int getWidth();
+ method @androidx.compose.runtime.Stable public operator long times(int other);
+ property @androidx.compose.runtime.Stable public final int height;
+ property @androidx.compose.runtime.Stable public final int width;
+ field public static final androidx.compose.ui.unit.IntSize.Companion Companion;
+ }
+
+ public static final class IntSize.Companion {
+ method public long getZero();
+ property public final long Zero;
+ }
+
+ public final class IntSizeKt {
+ method @androidx.compose.runtime.Stable public static long IntSize(int width, int height);
+ method public static long getCenter(long);
+ method @androidx.compose.runtime.Stable public static operator long times(int, long size);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.unit.IntRect toIntRect(long);
+ method @androidx.compose.runtime.Stable public static long toSize(long);
+ }
+
+ public enum LayoutDirection {
+ method public static androidx.compose.ui.unit.LayoutDirection valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.unit.LayoutDirection[] values();
+ enum_constant public static final androidx.compose.ui.unit.LayoutDirection Ltr;
+ enum_constant public static final androidx.compose.ui.unit.LayoutDirection Rtl;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TextUnit {
+ method public inline operator int compareTo(long other);
+ method public inline operator long div(double other);
+ method public inline operator long div(float other);
+ method public inline operator long div(int other);
+ method public long getType();
+ method public float getValue();
+ method public boolean isEm();
+ method public boolean isSp();
+ method public inline operator long times(double other);
+ method public inline operator long times(float other);
+ method public inline operator long times(int other);
+ method public inline operator long unaryMinus();
+ property public final boolean isEm;
+ property public final boolean isSp;
+ property public final long type;
+ property public final float value;
+ field public static final androidx.compose.ui.unit.TextUnit.Companion Companion;
+ }
+
+ public static final class TextUnit.Companion {
+ method public long getUnspecified();
+ property public final long Unspecified;
+ }
+
+ public final class TextUnitKt {
+ method public static long TextUnit(float value, long type);
+ method public static long getEm(double);
+ method public static long getEm(float);
+ method public static long getEm(int);
+ method public static long getSp(double);
+ method public static long getSp(float);
+ method public static long getSp(int);
+ method public static inline boolean isSpecified(long);
+ method public static boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.unit.TextUnit> block);
+ method @androidx.compose.runtime.Stable public static inline operator long times(double, long other);
+ method @androidx.compose.runtime.Stable public static inline operator long times(float, long other);
+ method @androidx.compose.runtime.Stable public static inline operator long times(int, long other);
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextUnitType {
+ ctor public TextUnitType(long type);
+ field public static final androidx.compose.ui.unit.TextUnitType.Companion Companion;
+ }
+
+ public static final class TextUnitType.Companion {
+ method public long getEm();
+ method public long getSp();
+ method public long getUnspecified();
+ property public final long Em;
+ property public final long Sp;
+ property public final long Unspecified;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Velocity {
+ method @androidx.compose.runtime.Stable public operator float component1();
+ method @androidx.compose.runtime.Stable public operator float component2();
+ method public long copy(optional float x, optional float y);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public float getX();
+ method public float getY();
+ method @androidx.compose.runtime.Stable public operator long minus(long other);
+ method @androidx.compose.runtime.Stable public operator long plus(long other);
+ method @androidx.compose.runtime.Stable public operator long rem(float operand);
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ method @androidx.compose.runtime.Stable public operator long unaryMinus();
+ property @androidx.compose.runtime.Stable public final float x;
+ property @androidx.compose.runtime.Stable public final float y;
+ field public static final androidx.compose.ui.unit.Velocity.Companion Companion;
+ }
+
+ public static final class Velocity.Companion {
+ method public long getZero();
+ property public final long Zero;
+ }
+
+ public final class VelocityKt {
+ method @androidx.compose.runtime.Stable public static long Velocity(float x, float y);
+ }
+
+}
+
diff --git a/compose/ui/ui-unit/api/restricted_1.5.0-beta01.txt b/compose/ui/ui-unit/api/restricted_1.5.0-beta01.txt
index fa535e7..c076a0f 100644
--- a/compose/ui/ui-unit/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui-unit/api/restricted_1.5.0-beta01.txt
@@ -194,9 +194,6 @@
property public final long Zero;
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalUnitApi {
- }
-
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntOffset {
method @androidx.compose.runtime.Stable public operator int component1();
method @androidx.compose.runtime.Stable public operator int component2();
diff --git a/compose/ui/ui-util/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-util/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..a4ae8f0
--- /dev/null
+++ b/compose/ui/ui-util/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.compose.ui.util {
+
+ public final class AndroidTrace_androidKt {
+ method public static inline <T> T trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+ }
+
+ public final class InlineClassHelperKt {
+ method public static inline long packFloats(float val1, float val2);
+ method public static inline long packInts(int val1, int val2);
+ method public static inline float unpackFloat1(long value);
+ method public static inline float unpackFloat2(long value);
+ method public static inline int unpackInt1(long value);
+ method public static inline int unpackInt2(long value);
+ }
+
+ public final class ListUtilsKt {
+ method public static inline <T> boolean fastAll(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public static inline <T> boolean fastAny(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public static inline <T> T? fastFirstOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public static inline <T> void fastForEach(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
+ method public static inline <T> void fastForEachIndexed(java.util.List<? extends T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+ method public static inline <T> void fastForEachReversed(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action);
+ method public static inline <T> T? fastLastOrNull(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> predicate);
+ method public static inline <T, R> java.util.List<R> fastMap(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
+ method public static inline <T, R, C extends java.util.Collection<? super R>> C fastMapTo(java.util.List<? extends T>, C destination, kotlin.jvm.functions.Function1<? super T,? extends R> transform);
+ method public static inline <T, R extends java.lang.Comparable<? super R>> T? fastMaxBy(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,? extends R> selector);
+ method public static inline <T> int fastSumBy(java.util.List<? extends T>, kotlin.jvm.functions.Function1<? super T,java.lang.Integer> selector);
+ }
+
+ public final class MathHelpersKt {
+ method public static float lerp(float start, float stop, float fraction);
+ method public static int lerp(int start, int stop, float fraction);
+ method public static long lerp(long start, long stop, float fraction);
+ }
+
+}
+
diff --git a/compose/ui/ui-viewbinding/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui-viewbinding/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..00c3178
--- /dev/null
+++ b/compose/ui/ui-viewbinding/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.compose.ui.viewinterop {
+
+ public final class AndroidViewBindingKt {
+ method @androidx.compose.runtime.Composable public static <T extends androidx.viewbinding.ViewBinding> void AndroidViewBinding(kotlin.jvm.functions.Function3<? super android.view.LayoutInflater,? super android.view.ViewGroup,? super java.lang.Boolean,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
+ method @androidx.compose.runtime.Composable public static <T extends androidx.viewbinding.ViewBinding> void AndroidViewBinding(kotlin.jvm.functions.Function3<? super android.view.LayoutInflater,? super android.view.ViewGroup,? super java.lang.Boolean,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onReset, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onRelease, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
+ }
+
+}
+
diff --git a/compose/ui/ui/api/1.5.0-beta01.txt b/compose/ui/ui/api/1.5.0-beta01.txt
index ad58ecf..e264a40 100644
--- a/compose/ui/ui/api/1.5.0-beta01.txt
+++ b/compose/ui/ui/api/1.5.0-beta01.txt
@@ -112,21 +112,11 @@
}
public final class ComposedModifierKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, Object? key3, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object![]? keys, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method @Deprecated public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalComposeUiApi {
- }
-
- @kotlin.RequiresOptIn(message="Unstable API for use only between compose-ui modules sharing the same exact version, " + "subject to change without notice in major, minor, or patch releases.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeUiApi {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
method public boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
method public boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
@@ -159,7 +149,6 @@
method public void onAttach();
method public void onDetach();
method public void onReset();
- method @androidx.compose.ui.ExperimentalComposeUiApi public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
property public final boolean isAttached;
property public final androidx.compose.ui.Modifier.Node node;
@@ -186,77 +175,6 @@
}
-package androidx.compose.ui.autofill {
-
- @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
- method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
- ctor public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
- method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
- method public androidx.compose.ui.geometry.Rect? getBoundingBox();
- method public int getId();
- method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
- method public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
- property public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
- property public final androidx.compose.ui.geometry.Rect? boundingBox;
- property public final int id;
- property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
- ctor public AutofillTree();
- method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
- method public kotlin.Unit? performAutofill(int id, String value);
- method public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
- property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
- method public static androidx.compose.ui.autofill.AutofillType valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.ui.autofill.AutofillType[] values();
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Gender;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewPassword;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewUsername;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Password;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Username;
- }
-
-}
-
package androidx.compose.ui.draw {
public final class AlphaKt {
@@ -359,22 +277,14 @@
public static final class FocusDirection.Companion {
method public int getDown();
- method @androidx.compose.ui.ExperimentalComposeUiApi public int getEnter();
- method @androidx.compose.ui.ExperimentalComposeUiApi public int getExit();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getIn();
method public int getLeft();
method public int getNext();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getOut();
method public int getPrevious();
method public int getRight();
method public int getUp();
property public final int Down;
- property @androidx.compose.ui.ExperimentalComposeUiApi public final int Enter;
- property @androidx.compose.ui.ExperimentalComposeUiApi public final int Exit;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int In;
property public final int Left;
property public final int Next;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Out;
property public final int Previous;
property public final int Right;
property public final int Up;
@@ -444,8 +354,6 @@
method public boolean getCanFocus();
method public default androidx.compose.ui.focus.FocusRequester getDown();
method public default androidx.compose.ui.focus.FocusRequester getEnd();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getEnter();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getExit();
method public default androidx.compose.ui.focus.FocusRequester getLeft();
method public default androidx.compose.ui.focus.FocusRequester getNext();
method public default androidx.compose.ui.focus.FocusRequester getPrevious();
@@ -455,8 +363,6 @@
method public void setCanFocus(boolean);
method public default void setDown(androidx.compose.ui.focus.FocusRequester);
method public default void setEnd(androidx.compose.ui.focus.FocusRequester);
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void setEnter(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void setExit(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
method public default void setLeft(androidx.compose.ui.focus.FocusRequester);
method public default void setNext(androidx.compose.ui.focus.FocusRequester);
method public default void setPrevious(androidx.compose.ui.focus.FocusRequester);
@@ -466,8 +372,6 @@
property public abstract boolean canFocus;
property public default androidx.compose.ui.focus.FocusRequester down;
property public default androidx.compose.ui.focus.FocusRequester end;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> enter;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> exit;
property public default androidx.compose.ui.focus.FocusRequester left;
property public default androidx.compose.ui.focus.FocusRequester next;
property public default androidx.compose.ui.focus.FocusRequester previous;
@@ -497,33 +401,10 @@
}
public static final class FocusRequester.Companion {
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory createRefs();
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester getCancel();
method public androidx.compose.ui.focus.FocusRequester getDefault();
- property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.focus.FocusRequester Cancel;
property public final androidx.compose.ui.focus.FocusRequester Default;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public static final class FocusRequester.Companion.FocusRequesterFactory {
- method public operator androidx.compose.ui.focus.FocusRequester component1();
- method public operator androidx.compose.ui.focus.FocusRequester component10();
- method public operator androidx.compose.ui.focus.FocusRequester component11();
- method public operator androidx.compose.ui.focus.FocusRequester component12();
- method public operator androidx.compose.ui.focus.FocusRequester component13();
- method public operator androidx.compose.ui.focus.FocusRequester component14();
- method public operator androidx.compose.ui.focus.FocusRequester component15();
- method public operator androidx.compose.ui.focus.FocusRequester component16();
- method public operator androidx.compose.ui.focus.FocusRequester component2();
- method public operator androidx.compose.ui.focus.FocusRequester component3();
- method public operator androidx.compose.ui.focus.FocusRequester component4();
- method public operator androidx.compose.ui.focus.FocusRequester component5();
- method public operator androidx.compose.ui.focus.FocusRequester component6();
- method public operator androidx.compose.ui.focus.FocusRequester component7();
- method public operator androidx.compose.ui.focus.FocusRequester component8();
- method public operator androidx.compose.ui.focus.FocusRequester component9();
- field public static final androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory INSTANCE;
- }
-
@Deprecated @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
method @Deprecated public androidx.compose.ui.focus.FocusRequester getFocusRequester();
property @Deprecated public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
@@ -945,7 +826,6 @@
public interface InputModeManager {
method public int getInputMode();
- method @androidx.compose.ui.ExperimentalComposeUiApi public boolean requestInputMode(int inputMode);
property public abstract int inputMode;
}
@@ -1581,16 +1461,6 @@
method public static int getNativeKeyCode(long);
}
- @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftKeyboardInterceptionModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public boolean onInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
- method public boolean onPreInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
- }
-
- public final class SoftwareKeyboardInterceptionModifierKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onInterceptKeyBeforeSoftKeyboard);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onPreInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreInterceptKeyBeforeSoftKeyboard);
- }
-
}
package androidx.compose.ui.input.nestedscroll {
@@ -1627,10 +1497,8 @@
public static final class NestedScrollSource.Companion {
method public int getDrag();
method public int getFling();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getRelocate();
property public final int Drag;
property public final int Fling;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Relocate;
}
}
@@ -1661,14 +1529,6 @@
property @Deprecated public final boolean positionChange;
}
- @androidx.compose.runtime.Immutable @androidx.compose.ui.ExperimentalComposeUiApi public final class HistoricalChange {
- ctor public HistoricalChange(long uptimeMillis, long position);
- method public long getPosition();
- method public long getUptimeMillis();
- property public final long position;
- property public final long uptimeMillis;
- }
-
@kotlin.jvm.JvmInline public final value class PointerButtons {
ctor public PointerButtons(int packedValue);
}
@@ -1798,14 +1658,11 @@
ctor @Deprecated 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 public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, boolean isInitiallyConsumed, optional int type, optional long scrollDelta);
method public void consume();
- 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 float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
method public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional long scrollDelta);
method @Deprecated 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 @Deprecated 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, androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type, optional long scrollDelta);
- 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 int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
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 int type, optional long scrollDelta);
method @Deprecated public androidx.compose.ui.input.pointer.ConsumedData getConsumed();
- method @androidx.compose.ui.ExperimentalComposeUiApi public java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> getHistorical();
method public long getId();
method public long getPosition();
method public boolean getPressed();
@@ -1818,7 +1675,6 @@
method public long getUptimeMillis();
method public boolean isConsumed();
property @Deprecated 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 public final boolean isConsumed;
property public final long position;
@@ -1862,11 +1718,6 @@
property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
}
- public final class PointerInteropFilter_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier motionEventSpy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,kotlin.Unit> watcher);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier pointerInteropFilter(androidx.compose.ui.Modifier, optional androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent? requestDisallowInterceptTouchEvent, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean> onTouchEvent);
- }
-
@kotlin.jvm.JvmInline public final value class PointerKeyboardModifiers {
ctor public PointerKeyboardModifiers(int packedValue);
}
@@ -1888,11 +1739,6 @@
property public final int Unknown;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class RequestDisallowInterceptTouchEvent implements kotlin.jvm.functions.Function1<java.lang.Boolean,kotlin.Unit> {
- ctor public RequestDisallowInterceptTouchEvent();
- method public void invoke(boolean disallowIntercept);
- }
-
public final class SuspendingPointerInputFilterKt {
method public static androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode SuspendingPointerInputModifierNode(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> pointerInputHandler);
method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
@@ -1930,9 +1776,6 @@
public final class VelocityTrackerKt {
method public static void addPointerInputChange(androidx.compose.ui.input.pointer.util.VelocityTracker, androidx.compose.ui.input.pointer.PointerInputChange event);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getVelocityTrackerAddPointsFix();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void setVelocityTrackerAddPointsFix(boolean);
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final boolean VelocityTrackerAddPointsFix;
}
}
@@ -2052,11 +1895,6 @@
ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
}
- @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface IntermediateMeasureScope extends androidx.compose.ui.layout.LookaheadScope kotlinx.coroutines.CoroutineScope androidx.compose.ui.layout.MeasureScope {
- method public long getLookaheadSize();
- property public abstract long lookaheadSize;
- }
-
public interface IntrinsicMeasurable {
method public Object? getParentData();
method public int maxIntrinsicHeight(int width);
@@ -2068,8 +1906,6 @@
public interface IntrinsicMeasureScope extends androidx.compose.ui.unit.Density {
method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead();
- property @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead;
property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
}
@@ -2156,27 +1992,6 @@
method public static androidx.compose.ui.Modifier layout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
}
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface LookaheadLayoutCoordinates extends androidx.compose.ui.layout.LayoutCoordinates {
- }
-
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadLayoutScope {
- method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadScope {
- method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method @Deprecated public default androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.ui.layout.MeasureResult> measure);
- method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
- method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
- method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
- }
-
- public final class LookaheadScopeKt {
- method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadLayout(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
- method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntermediateMeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
- }
-
public interface Measurable extends androidx.compose.ui.layout.IntrinsicMeasurable {
method public androidx.compose.ui.layout.Placeable measure(long constraints);
}
@@ -2311,24 +2126,6 @@
property protected abstract int parentWidth;
}
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi @kotlin.jvm.JvmDefaultWithCompatibility public interface RelocationModifier extends androidx.compose.ui.Modifier.Element {
- method @Deprecated public androidx.compose.ui.geometry.Rect computeDestination(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);
- method @Deprecated public suspend Object? performRelocation(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.geometry.Rect destination, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class RelocationModifierKt {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onRelocationRequest(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> onProvideDestination, kotlin.jvm.functions.Function3<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.geometry.Rect,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPerformRelocation);
- }
-
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class RelocationRequester {
- ctor @Deprecated public RelocationRequester();
- method @Deprecated public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class RelocationRequesterModifierKt {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier relocationRequester(androidx.compose.ui.Modifier, Object relocationRequester);
- }
-
public interface Remeasurement {
method public void forceRemeasure();
}
@@ -2420,10 +2217,6 @@
method public void onModifierLocalsUpdated(androidx.compose.ui.modifier.ModifierLocalReadScope scope);
}
- public final class ModifierLocalConsumerKt {
- method @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier modifierLocalConsumer(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.modifier.ModifierLocalReadScope,kotlin.Unit> consumer);
- }
-
public final class ModifierLocalKt {
method public static <T> androidx.compose.ui.modifier.ProvidableModifierLocal<T> modifierLocalOf(kotlin.jvm.functions.Function0<? extends T> defaultFactory);
}
@@ -2453,10 +2246,6 @@
property public abstract T value;
}
- public final class ModifierLocalProviderKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static <T> androidx.compose.ui.Modifier modifierLocalProvider(androidx.compose.ui.Modifier, androidx.compose.ui.modifier.ProvidableModifierLocal<T> key, kotlin.jvm.functions.Function0<? extends T> value);
- }
-
public interface ModifierLocalReadScope {
method public <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
}
@@ -2510,13 +2299,6 @@
method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalCoreApi {
- }
-
- @androidx.compose.ui.InternalComposeUiApi public sealed interface InteroperableComposeUiNode {
- method public android.view.View? getInteropView();
- }
-
public interface LayoutAwareModifierNode extends androidx.compose.ui.node.DelegatableNode {
method public default void onPlaced(androidx.compose.ui.layout.LayoutCoordinates coordinates);
method public default void onRemeasured(long size);
@@ -2588,7 +2370,6 @@
method public androidx.compose.ui.unit.Density getDensity();
method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
method public androidx.compose.ui.text.input.TextInputService getTextInputService();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void measureAndLayoutForTest();
method public boolean sendKeyEvent(android.view.KeyEvent keyEvent);
property public abstract androidx.compose.ui.unit.Density density;
property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
@@ -2705,8 +2486,6 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> getLocalAutofill();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> getLocalDensity();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> getLocalFocusManager();
@@ -2714,15 +2493,12 @@
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> getLocalHapticFeedback();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> getLocalInputModeManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> getLocalLayoutDirection();
- method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> getLocalPlatformTextInputPluginRegistry();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> getLocalTextInputService();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> getLocalTextToolbar();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> getLocalUriHandler();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> getLocalViewConfiguration();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> getLocalWindowInfo();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> LocalAccessibilityManager;
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> LocalAutofill;
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> LocalClipboardManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> LocalDensity;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> LocalFocusManager;
@@ -2730,7 +2506,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> LocalHapticFeedback;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> LocalInputModeManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> LocalLayoutDirection;
- property @androidx.compose.ui.text.ExperimentalTextApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> LocalPlatformTextInputPluginRegistry;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> LocalTextInputService;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> LocalTextToolbar;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> LocalUriHandler;
@@ -2801,24 +2576,10 @@
property public Object? valueOverride;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class LocalSoftwareKeyboardController {
- method @androidx.compose.runtime.Composable public androidx.compose.ui.platform.SoftwareKeyboardController? getCurrent();
- method public infix androidx.compose.runtime.ProvidedValue<androidx.compose.ui.platform.SoftwareKeyboardController> provides(androidx.compose.ui.platform.SoftwareKeyboardController softwareKeyboardController);
- property @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.platform.SoftwareKeyboardController? current;
- field public static final androidx.compose.ui.platform.LocalSoftwareKeyboardController INSTANCE;
- }
-
public final class NestedScrollInteropConnectionKt {
method @androidx.compose.runtime.Composable public static androidx.compose.ui.input.nestedscroll.NestedScrollConnection rememberNestedScrollInteropConnection(optional android.view.View hostView);
}
- @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftwareKeyboardController {
- method public void hide();
- method @Deprecated public default void hideSoftwareKeyboard();
- method public void show();
- method @Deprecated public default void showSoftwareKeyboard();
- }
-
public final class TestTagKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
}
@@ -2927,30 +2688,11 @@
}
@androidx.compose.runtime.Stable public interface WindowInfo {
- method @androidx.compose.ui.ExperimentalComposeUiApi public default int getKeyboardModifiers();
method public boolean isWindowFocused();
property public abstract boolean isWindowFocused;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default int keyboardModifiers;
- }
-
- @androidx.compose.ui.InternalComposeUiApi public fun interface WindowRecomposerFactory {
- method public androidx.compose.runtime.Recomposer createRecomposer(android.view.View windowRootView);
- field public static final androidx.compose.ui.platform.WindowRecomposerFactory.Companion Companion;
- }
-
- public static final class WindowRecomposerFactory.Companion {
- method public androidx.compose.ui.platform.WindowRecomposerFactory getLifecycleAware();
- property public final androidx.compose.ui.platform.WindowRecomposerFactory LifecycleAware;
- }
-
- @androidx.compose.ui.InternalComposeUiApi public final class WindowRecomposerPolicy {
- method public void setFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory);
- method public inline <R> R withFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory, kotlin.jvm.functions.Function0<? extends R> block);
- field public static final androidx.compose.ui.platform.WindowRecomposerPolicy INSTANCE;
}
public final class WindowRecomposer_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.Recomposer createLifecycleAwareWindowRecomposer(android.view.View, optional kotlin.coroutines.CoroutineContext coroutineContext, optional androidx.lifecycle.Lifecycle? lifecycle);
method public static androidx.compose.runtime.CompositionContext? findViewTreeCompositionContext(android.view.View);
method public static androidx.compose.runtime.CompositionContext? getCompositionContext(android.view.View);
method public static void setCompositionContext(android.view.View, androidx.compose.runtime.CompositionContext?);
@@ -3283,12 +3025,6 @@
field public static final androidx.compose.ui.semantics.SemanticsProperties INSTANCE;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class SemanticsPropertiesAndroid {
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getTestTagsAsResourceId();
- property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> TestTagsAsResourceId;
- field public static final androidx.compose.ui.semantics.SemanticsPropertiesAndroid INSTANCE;
- }
-
public final class SemanticsPropertiesKt {
method public static void collapse(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
method public static void copyText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3322,7 +3058,6 @@
method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method @Deprecated public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static boolean isTraversalGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3366,11 +3101,6 @@
method public static void setVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.ScrollAxisRange);
}
- public final class SemanticsProperties_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void setTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
- }
-
public final class SemanticsPropertyKey<T> {
ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
method public String getName();
@@ -3459,7 +3189,6 @@
@androidx.compose.runtime.Immutable public final class PopupProperties {
ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled);
- ctor @androidx.compose.ui.ExperimentalComposeUiApi public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled, optional boolean usePlatformDefaultWidth);
method public boolean getClippingEnabled();
method public boolean getDismissOnBackPress();
method public boolean getDismissOnClickOutside();
diff --git a/compose/ui/ui/api/public_plus_experimental_1.5.0-beta01.txt b/compose/ui/ui/api/public_plus_experimental_1.5.0-beta01.txt
new file mode 100644
index 0000000..434d4ce
--- /dev/null
+++ b/compose/ui/ui/api/public_plus_experimental_1.5.0-beta01.txt
@@ -0,0 +1,3491 @@
+// Signature format: 4.0
+package androidx.compose.ui {
+
+ public final class AbsoluteAlignment {
+ method public androidx.compose.ui.Alignment getBottomLeft();
+ method public androidx.compose.ui.Alignment getBottomRight();
+ method public androidx.compose.ui.Alignment getCenterLeft();
+ method public androidx.compose.ui.Alignment getCenterRight();
+ method public androidx.compose.ui.Alignment.Horizontal getLeft();
+ method public androidx.compose.ui.Alignment.Horizontal getRight();
+ method public androidx.compose.ui.Alignment getTopLeft();
+ method public androidx.compose.ui.Alignment getTopRight();
+ property public final androidx.compose.ui.Alignment BottomLeft;
+ property public final androidx.compose.ui.Alignment BottomRight;
+ property public final androidx.compose.ui.Alignment CenterLeft;
+ property public final androidx.compose.ui.Alignment CenterRight;
+ property public final androidx.compose.ui.Alignment.Horizontal Left;
+ property public final androidx.compose.ui.Alignment.Horizontal Right;
+ property public final androidx.compose.ui.Alignment TopLeft;
+ property public final androidx.compose.ui.Alignment TopRight;
+ field public static final androidx.compose.ui.AbsoluteAlignment INSTANCE;
+ }
+
+ @androidx.compose.runtime.Stable public fun interface Alignment {
+ method public long align(long size, long space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ field public static final androidx.compose.ui.Alignment.Companion Companion;
+ }
+
+ public static final class Alignment.Companion {
+ method public androidx.compose.ui.Alignment.Vertical getBottom();
+ method public androidx.compose.ui.Alignment getBottomCenter();
+ method public androidx.compose.ui.Alignment getBottomEnd();
+ method public androidx.compose.ui.Alignment getBottomStart();
+ method public androidx.compose.ui.Alignment getCenter();
+ method public androidx.compose.ui.Alignment getCenterEnd();
+ method public androidx.compose.ui.Alignment.Horizontal getCenterHorizontally();
+ method public androidx.compose.ui.Alignment getCenterStart();
+ method public androidx.compose.ui.Alignment.Vertical getCenterVertically();
+ method public androidx.compose.ui.Alignment.Horizontal getEnd();
+ method public androidx.compose.ui.Alignment.Horizontal getStart();
+ method public androidx.compose.ui.Alignment.Vertical getTop();
+ method public androidx.compose.ui.Alignment getTopCenter();
+ method public androidx.compose.ui.Alignment getTopEnd();
+ method public androidx.compose.ui.Alignment getTopStart();
+ property public final androidx.compose.ui.Alignment.Vertical Bottom;
+ property public final androidx.compose.ui.Alignment BottomCenter;
+ property public final androidx.compose.ui.Alignment BottomEnd;
+ property public final androidx.compose.ui.Alignment BottomStart;
+ property public final androidx.compose.ui.Alignment Center;
+ property public final androidx.compose.ui.Alignment CenterEnd;
+ property public final androidx.compose.ui.Alignment.Horizontal CenterHorizontally;
+ property public final androidx.compose.ui.Alignment CenterStart;
+ property public final androidx.compose.ui.Alignment.Vertical CenterVertically;
+ property public final androidx.compose.ui.Alignment.Horizontal End;
+ property public final androidx.compose.ui.Alignment.Horizontal Start;
+ property public final androidx.compose.ui.Alignment.Vertical Top;
+ property public final androidx.compose.ui.Alignment TopCenter;
+ property public final androidx.compose.ui.Alignment TopEnd;
+ property public final androidx.compose.ui.Alignment TopStart;
+ }
+
+ @androidx.compose.runtime.Stable public static fun interface Alignment.Horizontal {
+ method public int align(int size, int space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ }
+
+ @androidx.compose.runtime.Stable public static fun interface Alignment.Vertical {
+ method public int align(int size, int space);
+ }
+
+ @androidx.compose.runtime.Immutable public final class BiasAbsoluteAlignment implements androidx.compose.ui.Alignment {
+ ctor public BiasAbsoluteAlignment(float horizontalBias, float verticalBias);
+ method public long align(long size, long space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public androidx.compose.ui.BiasAbsoluteAlignment copy(float horizontalBias, float verticalBias);
+ }
+
+ @androidx.compose.runtime.Immutable public static final class BiasAbsoluteAlignment.Horizontal implements androidx.compose.ui.Alignment.Horizontal {
+ ctor public BiasAbsoluteAlignment.Horizontal(float bias);
+ method public int align(int size, int space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public androidx.compose.ui.BiasAbsoluteAlignment.Horizontal copy(float bias);
+ }
+
+ @androidx.compose.runtime.Immutable public final class BiasAlignment implements androidx.compose.ui.Alignment {
+ ctor public BiasAlignment(float horizontalBias, float verticalBias);
+ method public long align(long size, long space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public float component1();
+ method public float component2();
+ method public androidx.compose.ui.BiasAlignment copy(float horizontalBias, float verticalBias);
+ method public float getHorizontalBias();
+ method public float getVerticalBias();
+ property public final float horizontalBias;
+ property public final float verticalBias;
+ }
+
+ @androidx.compose.runtime.Immutable public static final class BiasAlignment.Horizontal implements androidx.compose.ui.Alignment.Horizontal {
+ ctor public BiasAlignment.Horizontal(float bias);
+ method public int align(int size, int space, androidx.compose.ui.unit.LayoutDirection layoutDirection);
+ method public androidx.compose.ui.BiasAlignment.Horizontal copy(float bias);
+ }
+
+ @androidx.compose.runtime.Immutable public static final class BiasAlignment.Vertical implements androidx.compose.ui.Alignment.Vertical {
+ ctor public BiasAlignment.Vertical(float bias);
+ method public int align(int size, int space);
+ method public androidx.compose.ui.BiasAlignment.Vertical copy(float bias);
+ }
+
+ public final class CombinedModifier implements androidx.compose.ui.Modifier {
+ ctor public CombinedModifier(androidx.compose.ui.Modifier outer, androidx.compose.ui.Modifier inner);
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.compose.ui.Modifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.compose.ui.Modifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class ComposedModifierKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, Object? key3, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object![]? keys, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method @Deprecated public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ }
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalComposeUiApi {
+ }
+
+ @kotlin.RequiresOptIn(message="Unstable API for use only between compose-ui modules sharing the same exact version, " + "subject to change without notice in major, minor, or patch releases.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeUiApi {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.compose.ui.Modifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.compose.ui.Modifier.Element,? super R,? extends R> operation);
+ method public default infix androidx.compose.ui.Modifier then(androidx.compose.ui.Modifier other);
+ field public static final androidx.compose.ui.Modifier.Companion Companion;
+ }
+
+ public static final class Modifier.Companion implements androidx.compose.ui.Modifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.compose.ui.Modifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.compose.ui.Modifier.Element,? super R,? extends R> operation);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public static interface Modifier.Element extends androidx.compose.ui.Modifier {
+ method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
+ method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.compose.ui.Modifier.Element,? extends R> operation);
+ method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.compose.ui.Modifier.Element,? super R,? extends R> operation);
+ }
+
+ public abstract static class Modifier.Node implements androidx.compose.ui.node.DelegatableNode {
+ ctor public Modifier.Node();
+ method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
+ method public final androidx.compose.ui.Modifier.Node getNode();
+ method public boolean getShouldAutoInvalidate();
+ method public final boolean isAttached();
+ method public void onAttach();
+ method public void onDetach();
+ method public void onReset();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+ property public final kotlinx.coroutines.CoroutineScope coroutineScope;
+ property public final boolean isAttached;
+ property public final androidx.compose.ui.Modifier.Node node;
+ property public boolean shouldAutoInvalidate;
+ }
+
+ @androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public float getScaleFactor();
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ property public abstract float scaleFactor;
+ field public static final androidx.compose.ui.MotionDurationScale.Key Key;
+ }
+
+ public static final class MotionDurationScale.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.MotionDurationScale> {
+ }
+
+ @androidx.compose.runtime.ComposableTargetMarker(description="UI Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface UiComposable {
+ }
+
+ public final class ZIndexModifierKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier zIndex(androidx.compose.ui.Modifier, float zIndex);
+ }
+
+}
+
+package androidx.compose.ui.autofill {
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
+ method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
+ ctor public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+ method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
+ method public androidx.compose.ui.geometry.Rect? getBoundingBox();
+ method public int getId();
+ method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
+ method public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
+ property public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
+ property public final androidx.compose.ui.geometry.Rect? boundingBox;
+ property public final int id;
+ property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
+ ctor public AutofillTree();
+ method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
+ method public kotlin.Unit? performAutofill(int id, String value);
+ method public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
+ method public static androidx.compose.ui.autofill.AutofillType valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.autofill.AutofillType[] values();
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType Gender;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType NewPassword;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType NewUsername;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType Password;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCode;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
+ enum_constant public static final androidx.compose.ui.autofill.AutofillType Username;
+ }
+
+}
+
+package androidx.compose.ui.draw {
+
+ public final class AlphaKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier alpha(androidx.compose.ui.Modifier, float alpha);
+ }
+
+ public final class BlurKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier blur(androidx.compose.ui.Modifier, float radius, optional androidx.compose.ui.graphics.Shape edgeTreatment);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier blur(androidx.compose.ui.Modifier, float radiusX, float radiusY, optional androidx.compose.ui.graphics.Shape edgeTreatment);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class BlurredEdgeTreatment {
+ ctor public BlurredEdgeTreatment(androidx.compose.ui.graphics.Shape? shape);
+ method public androidx.compose.ui.graphics.Shape? getShape();
+ property public final androidx.compose.ui.graphics.Shape? shape;
+ field public static final androidx.compose.ui.draw.BlurredEdgeTreatment.Companion Companion;
+ }
+
+ public static final class BlurredEdgeTreatment.Companion {
+ method public androidx.compose.ui.graphics.Shape getRectangle();
+ method public androidx.compose.ui.graphics.Shape getUnbounded();
+ property public final androidx.compose.ui.graphics.Shape Rectangle;
+ property public final androidx.compose.ui.graphics.Shape Unbounded;
+ }
+
+ public interface BuildDrawCacheParams {
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method public long getSize();
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ property public abstract long size;
+ }
+
+ public final class CacheDrawScope implements androidx.compose.ui.unit.Density {
+ method public float getDensity();
+ method public float getFontScale();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method public long getSize();
+ method public androidx.compose.ui.draw.DrawResult onDrawBehind(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> block);
+ method public androidx.compose.ui.draw.DrawResult onDrawWithContent(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> block);
+ property public float density;
+ property public float fontScale;
+ property public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ property public final long size;
+ }
+
+ public final class ClipKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier clip(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Shape shape);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier clipToBounds(androidx.compose.ui.Modifier);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface DrawCacheModifier extends androidx.compose.ui.draw.DrawModifier {
+ method public void onBuildCache(androidx.compose.ui.draw.BuildDrawCacheParams params);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface DrawModifier extends androidx.compose.ui.Modifier.Element {
+ method public void draw(androidx.compose.ui.graphics.drawscope.ContentDrawScope);
+ }
+
+ public final class DrawModifierKt {
+ method public static androidx.compose.ui.node.CacheDrawModifierNode CacheDrawModifierNode(kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
+ method public static androidx.compose.ui.Modifier drawBehind(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> onDraw);
+ method public static androidx.compose.ui.Modifier drawWithCache(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.draw.CacheDrawScope,androidx.compose.ui.draw.DrawResult> onBuildDrawCache);
+ method public static androidx.compose.ui.Modifier drawWithContent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.ContentDrawScope,kotlin.Unit> onDraw);
+ }
+
+ public final class DrawResult {
+ }
+
+ public final class PainterModifierKt {
+ method public static androidx.compose.ui.Modifier paint(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.painter.Painter painter, optional boolean sizeToIntrinsics, optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale, optional float alpha, optional androidx.compose.ui.graphics.ColorFilter? colorFilter);
+ }
+
+ public final class RotateKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier rotate(androidx.compose.ui.Modifier, float degrees);
+ }
+
+ public final class ScaleKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier scale(androidx.compose.ui.Modifier, float scale);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier scale(androidx.compose.ui.Modifier, float scaleX, float scaleY);
+ }
+
+ public final class ShadowKt {
+ method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier shadow(androidx.compose.ui.Modifier, float elevation, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier shadow(androidx.compose.ui.Modifier, float elevation, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip, optional long ambientColor, optional long spotColor);
+ }
+
+}
+
+package androidx.compose.ui.focus {
+
+ public final class FocusChangedModifierKt {
+ method public static androidx.compose.ui.Modifier onFocusChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusChanged);
+ }
+
+ @kotlin.jvm.JvmInline public final value class FocusDirection {
+ field public static final androidx.compose.ui.focus.FocusDirection.Companion Companion;
+ }
+
+ public static final class FocusDirection.Companion {
+ method public int getDown();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public int getEnter();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public int getExit();
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getIn();
+ method public int getLeft();
+ method public int getNext();
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getOut();
+ method public int getPrevious();
+ method public int getRight();
+ method public int getUp();
+ property public final int Down;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final int Enter;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final int Exit;
+ property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int In;
+ property public final int Left;
+ property public final int Next;
+ property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Out;
+ property public final int Previous;
+ property public final int Right;
+ property public final int Up;
+ }
+
+ @Deprecated @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusEventModifier extends androidx.compose.ui.Modifier.Element {
+ method @Deprecated public void onFocusEvent(androidx.compose.ui.focus.FocusState focusState);
+ }
+
+ public final class FocusEventModifierKt {
+ method public static androidx.compose.ui.Modifier onFocusEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusState,kotlin.Unit> onFocusEvent);
+ }
+
+ public interface FocusEventModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void onFocusEvent(androidx.compose.ui.focus.FocusState focusState);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusManager {
+ method public void clearFocus(optional boolean force);
+ method public boolean moveFocus(int focusDirection);
+ }
+
+ public final class FocusModifierKt {
+ method @Deprecated public static androidx.compose.ui.Modifier focusModifier(androidx.compose.ui.Modifier);
+ method public static androidx.compose.ui.Modifier focusTarget(androidx.compose.ui.Modifier);
+ }
+
+ @Deprecated public final class FocusOrder {
+ ctor @Deprecated public FocusOrder();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getDown();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getEnd();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getLeft();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getNext();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getPrevious();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getRight();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getStart();
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getUp();
+ method @Deprecated public void setDown(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setEnd(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setLeft(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setNext(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setPrevious(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setRight(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setStart(androidx.compose.ui.focus.FocusRequester);
+ method @Deprecated public void setUp(androidx.compose.ui.focus.FocusRequester);
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester down;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester end;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester left;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester next;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester previous;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester right;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester start;
+ property @Deprecated public final androidx.compose.ui.focus.FocusRequester up;
+ }
+
+ @Deprecated @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusOrderModifier extends androidx.compose.ui.Modifier.Element {
+ method @Deprecated public void populateFocusOrder(androidx.compose.ui.focus.FocusOrder focusOrder);
+ }
+
+ public final class FocusOrderModifierKt {
+ method @Deprecated public static androidx.compose.ui.Modifier focusOrder(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester);
+ method @Deprecated public static androidx.compose.ui.Modifier focusOrder(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusOrder,kotlin.Unit> focusOrderReceiver);
+ method @Deprecated public static androidx.compose.ui.Modifier focusOrder(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusOrder,kotlin.Unit> focusOrderReceiver);
+ }
+
+ public interface FocusProperties {
+ method public boolean getCanFocus();
+ method public default androidx.compose.ui.focus.FocusRequester getDown();
+ method public default androidx.compose.ui.focus.FocusRequester getEnd();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getEnter();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getExit();
+ method public default androidx.compose.ui.focus.FocusRequester getLeft();
+ method public default androidx.compose.ui.focus.FocusRequester getNext();
+ method public default androidx.compose.ui.focus.FocusRequester getPrevious();
+ method public default androidx.compose.ui.focus.FocusRequester getRight();
+ method public default androidx.compose.ui.focus.FocusRequester getStart();
+ method public default androidx.compose.ui.focus.FocusRequester getUp();
+ method public void setCanFocus(boolean);
+ method public default void setDown(androidx.compose.ui.focus.FocusRequester);
+ method public default void setEnd(androidx.compose.ui.focus.FocusRequester);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default void setEnter(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default void setExit(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
+ method public default void setLeft(androidx.compose.ui.focus.FocusRequester);
+ method public default void setNext(androidx.compose.ui.focus.FocusRequester);
+ method public default void setPrevious(androidx.compose.ui.focus.FocusRequester);
+ method public default void setRight(androidx.compose.ui.focus.FocusRequester);
+ method public default void setStart(androidx.compose.ui.focus.FocusRequester);
+ method public default void setUp(androidx.compose.ui.focus.FocusRequester);
+ property public abstract boolean canFocus;
+ property public default androidx.compose.ui.focus.FocusRequester down;
+ property public default androidx.compose.ui.focus.FocusRequester end;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> enter;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> exit;
+ property public default androidx.compose.ui.focus.FocusRequester left;
+ property public default androidx.compose.ui.focus.FocusRequester next;
+ property public default androidx.compose.ui.focus.FocusRequester previous;
+ property public default androidx.compose.ui.focus.FocusRequester right;
+ property public default androidx.compose.ui.focus.FocusRequester start;
+ property public default androidx.compose.ui.focus.FocusRequester up;
+ }
+
+ public final class FocusPropertiesKt {
+ method public static androidx.compose.ui.Modifier focusProperties(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusProperties,kotlin.Unit> scope);
+ }
+
+ public interface FocusPropertiesModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void applyFocusProperties(androidx.compose.ui.focus.FocusProperties focusProperties);
+ }
+
+ public final class FocusPropertiesModifierNodeKt {
+ method public static void invalidateFocusProperties(androidx.compose.ui.focus.FocusPropertiesModifierNode);
+ }
+
+ @androidx.compose.runtime.Stable public final class FocusRequester {
+ ctor public FocusRequester();
+ method public boolean captureFocus();
+ method public boolean freeFocus();
+ method public void requestFocus();
+ field public static final androidx.compose.ui.focus.FocusRequester.Companion Companion;
+ }
+
+ public static final class FocusRequester.Companion {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory createRefs();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester getCancel();
+ method public androidx.compose.ui.focus.FocusRequester getDefault();
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.focus.FocusRequester Cancel;
+ property public final androidx.compose.ui.focus.FocusRequester Default;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public static final class FocusRequester.Companion.FocusRequesterFactory {
+ method public operator androidx.compose.ui.focus.FocusRequester component1();
+ method public operator androidx.compose.ui.focus.FocusRequester component10();
+ method public operator androidx.compose.ui.focus.FocusRequester component11();
+ method public operator androidx.compose.ui.focus.FocusRequester component12();
+ method public operator androidx.compose.ui.focus.FocusRequester component13();
+ method public operator androidx.compose.ui.focus.FocusRequester component14();
+ method public operator androidx.compose.ui.focus.FocusRequester component15();
+ method public operator androidx.compose.ui.focus.FocusRequester component16();
+ method public operator androidx.compose.ui.focus.FocusRequester component2();
+ method public operator androidx.compose.ui.focus.FocusRequester component3();
+ method public operator androidx.compose.ui.focus.FocusRequester component4();
+ method public operator androidx.compose.ui.focus.FocusRequester component5();
+ method public operator androidx.compose.ui.focus.FocusRequester component6();
+ method public operator androidx.compose.ui.focus.FocusRequester component7();
+ method public operator androidx.compose.ui.focus.FocusRequester component8();
+ method public operator androidx.compose.ui.focus.FocusRequester component9();
+ field public static final androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory INSTANCE;
+ }
+
+ @Deprecated @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
+ method @Deprecated public androidx.compose.ui.focus.FocusRequester getFocusRequester();
+ property @Deprecated public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
+ }
+
+ public final class FocusRequesterModifierKt {
+ method public static androidx.compose.ui.Modifier focusRequester(androidx.compose.ui.Modifier, androidx.compose.ui.focus.FocusRequester focusRequester);
+ }
+
+ public interface FocusRequesterModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ }
+
+ public final class FocusRequesterModifierNodeKt {
+ method public static boolean captureFocus(androidx.compose.ui.focus.FocusRequesterModifierNode);
+ method public static boolean freeFocus(androidx.compose.ui.focus.FocusRequesterModifierNode);
+ method public static boolean requestFocus(androidx.compose.ui.focus.FocusRequesterModifierNode);
+ }
+
+ public interface FocusState {
+ method public boolean getHasFocus();
+ method public boolean isCaptured();
+ method public boolean isFocused();
+ property public abstract boolean hasFocus;
+ property public abstract boolean isCaptured;
+ property public abstract boolean isFocused;
+ }
+
+ public final class FocusTargetNode extends androidx.compose.ui.Modifier.Node implements androidx.compose.ui.modifier.ModifierLocalModifierNode androidx.compose.ui.node.ObserverModifierNode {
+ ctor public FocusTargetNode();
+ method public androidx.compose.ui.focus.FocusState getFocusState();
+ method public void onObservedReadsChanged();
+ property public final androidx.compose.ui.focus.FocusState focusState;
+ }
+
+}
+
+package androidx.compose.ui.graphics {
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class CompositingStrategy {
+ field public static final androidx.compose.ui.graphics.CompositingStrategy.Companion Companion;
+ }
+
+ public static final class CompositingStrategy.Companion {
+ method public int getAuto();
+ method public int getModulateAlpha();
+ method public int getOffscreen();
+ property public final int Auto;
+ property public final int ModulateAlpha;
+ property public final int Offscreen;
+ }
+
+ public final class GraphicsLayerModifierKt {
+ method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier graphicsLayer(androidx.compose.ui.Modifier, optional float scaleX, optional float scaleY, optional float alpha, optional float translationX, optional float translationY, optional float shadowElevation, optional float rotationX, optional float rotationY, optional float rotationZ, optional float cameraDistance, optional long transformOrigin, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip);
+ method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier graphicsLayer(androidx.compose.ui.Modifier, optional float scaleX, optional float scaleY, optional float alpha, optional float translationX, optional float translationY, optional float shadowElevation, optional float rotationX, optional float rotationY, optional float rotationZ, optional float cameraDistance, optional long transformOrigin, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip, optional androidx.compose.ui.graphics.RenderEffect? renderEffect);
+ method @Deprecated @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier graphicsLayer(androidx.compose.ui.Modifier, optional float scaleX, optional float scaleY, optional float alpha, optional float translationX, optional float translationY, optional float shadowElevation, optional float rotationX, optional float rotationY, optional float rotationZ, optional float cameraDistance, optional long transformOrigin, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip, optional androidx.compose.ui.graphics.RenderEffect? renderEffect, optional long ambientShadowColor, optional long spotShadowColor);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier graphicsLayer(androidx.compose.ui.Modifier, optional float scaleX, optional float scaleY, optional float alpha, optional float translationX, optional float translationY, optional float shadowElevation, optional float rotationX, optional float rotationY, optional float rotationZ, optional float cameraDistance, optional long transformOrigin, optional androidx.compose.ui.graphics.Shape shape, optional boolean clip, optional androidx.compose.ui.graphics.RenderEffect? renderEffect, optional long ambientShadowColor, optional long spotShadowColor, optional int compositingStrategy);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier graphicsLayer(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> block);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier toolingGraphicsLayer(androidx.compose.ui.Modifier);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface GraphicsLayerScope extends androidx.compose.ui.unit.Density {
+ method public float getAlpha();
+ method public default long getAmbientShadowColor();
+ method public float getCameraDistance();
+ method public boolean getClip();
+ method public default int getCompositingStrategy();
+ method public default androidx.compose.ui.graphics.RenderEffect? getRenderEffect();
+ method public float getRotationX();
+ method public float getRotationY();
+ method public float getRotationZ();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public float getShadowElevation();
+ method public androidx.compose.ui.graphics.Shape getShape();
+ method public default long getSize();
+ method public default long getSpotShadowColor();
+ method public long getTransformOrigin();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public void setAlpha(float);
+ method public default void setAmbientShadowColor(long);
+ method public void setCameraDistance(float);
+ method public void setClip(boolean);
+ method public default void setCompositingStrategy(int);
+ method public default void setRenderEffect(androidx.compose.ui.graphics.RenderEffect?);
+ method public void setRotationX(float);
+ method public void setRotationY(float);
+ method public void setRotationZ(float);
+ method public void setScaleX(float);
+ method public void setScaleY(float);
+ method public void setShadowElevation(float);
+ method public void setShape(androidx.compose.ui.graphics.Shape);
+ method public default void setSpotShadowColor(long);
+ method public void setTransformOrigin(long);
+ method public void setTranslationX(float);
+ method public void setTranslationY(float);
+ property public abstract float alpha;
+ property public default long ambientShadowColor;
+ property public abstract float cameraDistance;
+ property public abstract boolean clip;
+ property public default int compositingStrategy;
+ property public default androidx.compose.ui.graphics.RenderEffect? renderEffect;
+ property public abstract float rotationX;
+ property public abstract float rotationY;
+ property public abstract float rotationZ;
+ property public abstract float scaleX;
+ property public abstract float scaleY;
+ property public abstract float shadowElevation;
+ property public abstract androidx.compose.ui.graphics.Shape shape;
+ property public default long size;
+ property public default long spotShadowColor;
+ property public abstract long transformOrigin;
+ property public abstract float translationX;
+ property public abstract float translationY;
+ }
+
+ public final class GraphicsLayerScopeKt {
+ method public static androidx.compose.ui.graphics.GraphicsLayerScope GraphicsLayerScope();
+ method public static long getDefaultShadowColor();
+ property public static final long DefaultShadowColor;
+ field public static final float DefaultCameraDistance = 8.0f;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TransformOrigin {
+ method @androidx.compose.runtime.Stable public inline operator float component1();
+ method @androidx.compose.runtime.Stable public inline operator float component2();
+ method public long copy(optional float pivotFractionX, optional float pivotFractionY);
+ method public float getPivotFractionX();
+ method public float getPivotFractionY();
+ property public final float pivotFractionX;
+ property public final float pivotFractionY;
+ field public static final androidx.compose.ui.graphics.TransformOrigin.Companion Companion;
+ }
+
+ public static final class TransformOrigin.Companion {
+ method public long getCenter();
+ property public final long Center;
+ }
+
+ public final class TransformOriginKt {
+ method public static long TransformOrigin(float pivotFractionX, float pivotFractionY);
+ }
+
+}
+
+package androidx.compose.ui.graphics.vector {
+
+ @androidx.compose.runtime.Immutable public final class ImageVector {
+ method public boolean getAutoMirror();
+ method public float getDefaultHeight();
+ method public float getDefaultWidth();
+ method public String getName();
+ method public androidx.compose.ui.graphics.vector.VectorGroup getRoot();
+ method public int getTintBlendMode();
+ method public long getTintColor();
+ method public float getViewportHeight();
+ method public float getViewportWidth();
+ property public final boolean autoMirror;
+ property public final float defaultHeight;
+ property public final float defaultWidth;
+ property public final String name;
+ property public final androidx.compose.ui.graphics.vector.VectorGroup root;
+ property public final int tintBlendMode;
+ property public final long tintColor;
+ property public final float viewportHeight;
+ property public final float viewportWidth;
+ field public static final androidx.compose.ui.graphics.vector.ImageVector.Companion Companion;
+ }
+
+ public static final class ImageVector.Builder {
+ ctor @Deprecated public ImageVector.Builder(optional String name, float defaultWidth, float defaultHeight, float viewportWidth, float viewportHeight, optional long tintColor, optional int tintBlendMode);
+ ctor public ImageVector.Builder(optional String name, float defaultWidth, float defaultHeight, float viewportWidth, float viewportHeight, optional long tintColor, optional int tintBlendMode, optional boolean autoMirror);
+ method public androidx.compose.ui.graphics.vector.ImageVector.Builder addGroup(optional String name, optional float rotate, optional float pivotX, optional float pivotY, optional float scaleX, optional float scaleY, optional float translationX, optional float translationY, optional java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> clipPathData);
+ method public androidx.compose.ui.graphics.vector.ImageVector.Builder addPath(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> pathData, optional int pathFillType, optional String name, optional androidx.compose.ui.graphics.Brush? fill, optional float fillAlpha, optional androidx.compose.ui.graphics.Brush? stroke, optional float strokeAlpha, optional float strokeLineWidth, optional int strokeLineCap, optional int strokeLineJoin, optional float strokeLineMiter, optional float trimPathStart, optional float trimPathEnd, optional float trimPathOffset);
+ method public androidx.compose.ui.graphics.vector.ImageVector build();
+ method public androidx.compose.ui.graphics.vector.ImageVector.Builder clearGroup();
+ }
+
+ public static final class ImageVector.Companion {
+ }
+
+ public final class ImageVectorKt {
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector.Builder group(androidx.compose.ui.graphics.vector.ImageVector.Builder, optional String name, optional float rotate, optional float pivotX, optional float pivotY, optional float scaleX, optional float scaleY, optional float translationX, optional float translationY, optional java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> clipPathData, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,kotlin.Unit> block);
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector.Builder path(androidx.compose.ui.graphics.vector.ImageVector.Builder, optional String name, optional androidx.compose.ui.graphics.Brush? fill, optional float fillAlpha, optional androidx.compose.ui.graphics.Brush? stroke, optional float strokeAlpha, optional float strokeLineWidth, optional int strokeLineCap, optional int strokeLineJoin, optional float strokeLineMiter, optional int pathFillType, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.PathBuilder,kotlin.Unit> pathBuilder);
+ }
+
+ public abstract sealed class VNode {
+ method public abstract void draw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ method public final void invalidate();
+ }
+
+ public final class VectorApplier extends androidx.compose.runtime.AbstractApplier<androidx.compose.ui.graphics.vector.VNode> {
+ ctor public VectorApplier(androidx.compose.ui.graphics.vector.VNode root);
+ method public void insertBottomUp(int index, androidx.compose.ui.graphics.vector.VNode instance);
+ method public void insertTopDown(int index, androidx.compose.ui.graphics.vector.VNode instance);
+ method public void move(int from, int to, int count);
+ method protected void onClear();
+ method public void remove(int index, int count);
+ }
+
+ @androidx.compose.runtime.ComposableTargetMarker(description="Vector Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface VectorComposable {
+ }
+
+ public final class VectorComposeKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.graphics.vector.VectorComposable public static void Group(optional String name, optional float rotation, optional float pivotX, optional float pivotY, optional float scaleX, optional float scaleY, optional float translationX, optional float translationY, optional java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> clipPathData, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.graphics.vector.VectorComposable public static void Path(java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode> pathData, optional int pathFillType, optional String name, optional androidx.compose.ui.graphics.Brush? fill, optional float fillAlpha, optional androidx.compose.ui.graphics.Brush? stroke, optional float strokeAlpha, optional float strokeLineWidth, optional int strokeLineCap, optional int strokeLineJoin, optional float strokeLineMiter, optional float trimPathStart, optional float trimPathEnd, optional float trimPathOffset);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface VectorConfig {
+ method public default <T> T getOrDefault(androidx.compose.ui.graphics.vector.VectorProperty<T> property, T defaultValue);
+ }
+
+ @androidx.compose.runtime.Immutable public final class VectorGroup extends androidx.compose.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.compose.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+ method public operator androidx.compose.ui.graphics.vector.VectorNode get(int index);
+ method public java.util.List<androidx.compose.ui.graphics.vector.PathNode> getClipPathData();
+ method public String getName();
+ method public float getPivotX();
+ method public float getPivotY();
+ method public float getRotation();
+ method public float getScaleX();
+ method public float getScaleY();
+ method public int getSize();
+ method public float getTranslationX();
+ method public float getTranslationY();
+ method public java.util.Iterator<androidx.compose.ui.graphics.vector.VectorNode> iterator();
+ property public final java.util.List<androidx.compose.ui.graphics.vector.PathNode> clipPathData;
+ property public final String name;
+ property public final float pivotX;
+ property public final float pivotY;
+ property public final float rotation;
+ property public final float scaleX;
+ property public final float scaleY;
+ property public final int size;
+ property public final float translationX;
+ property public final float translationY;
+ }
+
+ public final class VectorKt {
+ method public static inline java.util.List<androidx.compose.ui.graphics.vector.PathNode> PathData(kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.PathBuilder,kotlin.Unit> block);
+ method public static java.util.List<androidx.compose.ui.graphics.vector.PathNode> addPathNodes(String? pathStr);
+ method public static int getDefaultFillType();
+ method public static int getDefaultStrokeLineCap();
+ method public static int getDefaultStrokeLineJoin();
+ method public static int getDefaultTintBlendMode();
+ method public static long getDefaultTintColor();
+ method public static java.util.List<androidx.compose.ui.graphics.vector.PathNode> getEmptyPath();
+ property public static final int DefaultFillType;
+ property public static final int DefaultStrokeLineCap;
+ property public static final int DefaultStrokeLineJoin;
+ property public static final int DefaultTintBlendMode;
+ property public static final long DefaultTintColor;
+ property public static final java.util.List<androidx.compose.ui.graphics.vector.PathNode> EmptyPath;
+ field public static final String DefaultGroupName = "";
+ field public static final String DefaultPathName = "";
+ field public static final float DefaultPivotX = 0.0f;
+ field public static final float DefaultPivotY = 0.0f;
+ field public static final float DefaultRotation = 0.0f;
+ field public static final float DefaultScaleX = 1.0f;
+ field public static final float DefaultScaleY = 1.0f;
+ field public static final float DefaultStrokeLineMiter = 4.0f;
+ field public static final float DefaultStrokeLineWidth = 0.0f;
+ field public static final float DefaultTranslationX = 0.0f;
+ field public static final float DefaultTranslationY = 0.0f;
+ field public static final float DefaultTrimPathEnd = 1.0f;
+ field public static final float DefaultTrimPathOffset = 0.0f;
+ field public static final float DefaultTrimPathStart = 0.0f;
+ }
+
+ public abstract sealed class VectorNode {
+ }
+
+ public final class VectorPainter extends androidx.compose.ui.graphics.painter.Painter {
+ method public long getIntrinsicSize();
+ method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);
+ property public long intrinsicSize;
+ }
+
+ public final class VectorPainterKt {
+ method @androidx.compose.runtime.Composable public static void RenderVectorGroup(androidx.compose.ui.graphics.vector.VectorGroup group, optional java.util.Map<java.lang.String,? extends androidx.compose.ui.graphics.vector.VectorConfig> configs);
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.vector.VectorPainter rememberVectorPainter(androidx.compose.ui.graphics.vector.ImageVector image);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableOpenTarget(index=0xffffffff) public static androidx.compose.ui.graphics.vector.VectorPainter rememberVectorPainter(float defaultWidth, float defaultHeight, optional float viewportWidth, optional float viewportHeight, optional String name, optional long tintColor, optional int tintBlendMode, optional boolean autoMirror, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.ComposableOpenTarget(index=0xffffffff) public static androidx.compose.ui.graphics.vector.VectorPainter rememberVectorPainter(float defaultWidth, float defaultHeight, optional float viewportWidth, optional float viewportHeight, optional String name, optional long tintColor, optional int tintBlendMode, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,kotlin.Unit> content);
+ field public static final String RootGroupName = "VectorRootGroup";
+ }
+
+ @androidx.compose.runtime.Immutable public final class VectorPath extends androidx.compose.ui.graphics.vector.VectorNode {
+ method public androidx.compose.ui.graphics.Brush? getFill();
+ method public float getFillAlpha();
+ method public String getName();
+ method public java.util.List<androidx.compose.ui.graphics.vector.PathNode> getPathData();
+ method public int getPathFillType();
+ method public androidx.compose.ui.graphics.Brush? getStroke();
+ method public float getStrokeAlpha();
+ method public int getStrokeLineCap();
+ method public int getStrokeLineJoin();
+ method public float getStrokeLineMiter();
+ method public float getStrokeLineWidth();
+ method public float getTrimPathEnd();
+ method public float getTrimPathOffset();
+ method public float getTrimPathStart();
+ property public final androidx.compose.ui.graphics.Brush? fill;
+ property public final float fillAlpha;
+ property public final String name;
+ property public final java.util.List<androidx.compose.ui.graphics.vector.PathNode> pathData;
+ property public final int pathFillType;
+ property public final androidx.compose.ui.graphics.Brush? stroke;
+ property public final float strokeAlpha;
+ property public final int strokeLineCap;
+ property public final int strokeLineJoin;
+ property public final float strokeLineMiter;
+ property public final float strokeLineWidth;
+ property public final float trimPathEnd;
+ property public final float trimPathOffset;
+ property public final float trimPathStart;
+ }
+
+ public abstract sealed class VectorProperty<T> {
+ }
+
+ public static final class VectorProperty.Fill extends androidx.compose.ui.graphics.vector.VectorProperty<androidx.compose.ui.graphics.Brush> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.Fill INSTANCE;
+ }
+
+ public static final class VectorProperty.FillAlpha extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.FillAlpha INSTANCE;
+ }
+
+ public static final class VectorProperty.PathData extends androidx.compose.ui.graphics.vector.VectorProperty<java.util.List<? extends androidx.compose.ui.graphics.vector.PathNode>> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.PathData INSTANCE;
+ }
+
+ public static final class VectorProperty.PivotX extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.PivotX INSTANCE;
+ }
+
+ public static final class VectorProperty.PivotY extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.PivotY INSTANCE;
+ }
+
+ public static final class VectorProperty.Rotation extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.Rotation INSTANCE;
+ }
+
+ public static final class VectorProperty.ScaleX extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.ScaleX INSTANCE;
+ }
+
+ public static final class VectorProperty.ScaleY extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.ScaleY INSTANCE;
+ }
+
+ public static final class VectorProperty.Stroke extends androidx.compose.ui.graphics.vector.VectorProperty<androidx.compose.ui.graphics.Brush> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.Stroke INSTANCE;
+ }
+
+ public static final class VectorProperty.StrokeAlpha extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.StrokeAlpha INSTANCE;
+ }
+
+ public static final class VectorProperty.StrokeLineWidth extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.StrokeLineWidth INSTANCE;
+ }
+
+ public static final class VectorProperty.TranslateX extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.TranslateX INSTANCE;
+ }
+
+ public static final class VectorProperty.TranslateY extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.TranslateY INSTANCE;
+ }
+
+ public static final class VectorProperty.TrimPathEnd extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.TrimPathEnd INSTANCE;
+ }
+
+ public static final class VectorProperty.TrimPathOffset extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.TrimPathOffset INSTANCE;
+ }
+
+ public static final class VectorProperty.TrimPathStart extends androidx.compose.ui.graphics.vector.VectorProperty<java.lang.Float> {
+ field public static final androidx.compose.ui.graphics.vector.VectorProperty.TrimPathStart INSTANCE;
+ }
+
+}
+
+package androidx.compose.ui.hapticfeedback {
+
+ public interface HapticFeedback {
+ method public void performHapticFeedback(int hapticFeedbackType);
+ }
+
+ @kotlin.jvm.JvmInline public final value class HapticFeedbackType {
+ ctor public HapticFeedbackType(int value);
+ field public static final androidx.compose.ui.hapticfeedback.HapticFeedbackType.Companion Companion;
+ }
+
+ public static final class HapticFeedbackType.Companion {
+ method public int getLongPress();
+ method public int getTextHandleMove();
+ method public java.util.List<androidx.compose.ui.hapticfeedback.HapticFeedbackType> values();
+ property public final int LongPress;
+ property public final int TextHandleMove;
+ }
+
+}
+
+package androidx.compose.ui.input {
+
+ @kotlin.jvm.JvmInline public final value class InputMode {
+ field public static final androidx.compose.ui.input.InputMode.Companion Companion;
+ }
+
+ public static final class InputMode.Companion {
+ method public int getKeyboard();
+ method public int getTouch();
+ property public final int Keyboard;
+ property public final int Touch;
+ }
+
+ public interface InputModeManager {
+ method public int getInputMode();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public boolean requestInputMode(int inputMode);
+ property public abstract int inputMode;
+ }
+
+}
+
+package androidx.compose.ui.input.key {
+
+ @kotlin.jvm.JvmInline public final value class Key {
+ ctor public Key(long keyCode);
+ method public long getKeyCode();
+ property public final long keyCode;
+ field public static final androidx.compose.ui.input.key.Key.Companion Companion;
+ }
+
+ public static final class Key.Companion {
+ method public long getA();
+ method public long getAllApps();
+ method public long getAltLeft();
+ method public long getAltRight();
+ method public long getApostrophe();
+ method public long getAppSwitch();
+ method public long getAssist();
+ method public long getAt();
+ method public long getAvReceiverInput();
+ method public long getAvReceiverPower();
+ method public long getB();
+ method public long getBack();
+ method public long getBackslash();
+ method public long getBackspace();
+ method public long getBookmark();
+ method public long getBreak();
+ method public long getBrightnessDown();
+ method public long getBrightnessUp();
+ method public long getBrowser();
+ method public long getButton1();
+ method public long getButton10();
+ method public long getButton11();
+ method public long getButton12();
+ method public long getButton13();
+ method public long getButton14();
+ method public long getButton15();
+ method public long getButton16();
+ method public long getButton2();
+ method public long getButton3();
+ method public long getButton4();
+ method public long getButton5();
+ method public long getButton6();
+ method public long getButton7();
+ method public long getButton8();
+ method public long getButton9();
+ method public long getButtonA();
+ method public long getButtonB();
+ method public long getButtonC();
+ method public long getButtonL1();
+ method public long getButtonL2();
+ method public long getButtonMode();
+ method public long getButtonR1();
+ method public long getButtonR2();
+ method public long getButtonSelect();
+ method public long getButtonStart();
+ method public long getButtonThumbLeft();
+ method public long getButtonThumbRight();
+ method public long getButtonX();
+ method public long getButtonY();
+ method public long getButtonZ();
+ method public long getC();
+ method public long getCalculator();
+ method public long getCalendar();
+ method public long getCall();
+ method public long getCamera();
+ method public long getCapsLock();
+ method public long getCaptions();
+ method public long getChannelDown();
+ method public long getChannelUp();
+ method public long getClear();
+ method public long getComma();
+ method public long getContacts();
+ method public long getCopy();
+ method public long getCtrlLeft();
+ method public long getCtrlRight();
+ method public long getCut();
+ method public long getD();
+ method public long getDelete();
+ method public long getDirectionCenter();
+ method public long getDirectionDown();
+ method public long getDirectionDownLeft();
+ method public long getDirectionDownRight();
+ method public long getDirectionLeft();
+ method public long getDirectionRight();
+ method public long getDirectionUp();
+ method public long getDirectionUpLeft();
+ method public long getDirectionUpRight();
+ method public long getDvr();
+ method public long getE();
+ method public long getEight();
+ method public long getEisu();
+ method public long getEndCall();
+ method public long getEnter();
+ method public long getEnvelope();
+ method public long getEquals();
+ method public long getEscape();
+ method public long getF();
+ method public long getF1();
+ method public long getF10();
+ method public long getF11();
+ method public long getF12();
+ method public long getF2();
+ method public long getF3();
+ method public long getF4();
+ method public long getF5();
+ method public long getF6();
+ method public long getF7();
+ method public long getF8();
+ method public long getF9();
+ method public long getFive();
+ method public long getFocus();
+ method public long getForward();
+ method public long getFour();
+ method public long getFunction();
+ method public long getG();
+ method public long getGrave();
+ method public long getGuide();
+ method public long getH();
+ method public long getHeadsetHook();
+ method public long getHelp();
+ method public long getHenkan();
+ method public long getHome();
+ method public long getI();
+ method public long getInfo();
+ method public long getInsert();
+ method public long getJ();
+ method public long getK();
+ method public long getKana();
+ method public long getKatakanaHiragana();
+ method public long getL();
+ method public long getLanguageSwitch();
+ method public long getLastChannel();
+ method public long getLeftBracket();
+ method public long getM();
+ method public long getMannerMode();
+ method public long getMediaAudioTrack();
+ method public long getMediaClose();
+ method public long getMediaEject();
+ method public long getMediaFastForward();
+ method public long getMediaNext();
+ method public long getMediaPause();
+ method public long getMediaPlay();
+ method public long getMediaPlayPause();
+ method public long getMediaPrevious();
+ method public long getMediaRecord();
+ method public long getMediaRewind();
+ method public long getMediaSkipBackward();
+ method public long getMediaSkipForward();
+ method public long getMediaStepBackward();
+ method public long getMediaStepForward();
+ method public long getMediaStop();
+ method public long getMediaTopMenu();
+ method public long getMenu();
+ method public long getMetaLeft();
+ method public long getMetaRight();
+ method public long getMicrophoneMute();
+ method public long getMinus();
+ method public long getMoveEnd();
+ method public long getMoveHome();
+ method public long getMuhenkan();
+ method public long getMultiply();
+ method public long getMusic();
+ method public long getN();
+ method public long getNavigateIn();
+ method public long getNavigateNext();
+ method public long getNavigateOut();
+ method public long getNavigatePrevious();
+ method public long getNine();
+ method public long getNotification();
+ method public long getNumLock();
+ method public long getNumPad0();
+ method public long getNumPad1();
+ method public long getNumPad2();
+ method public long getNumPad3();
+ method public long getNumPad4();
+ method public long getNumPad5();
+ method public long getNumPad6();
+ method public long getNumPad7();
+ method public long getNumPad8();
+ method public long getNumPad9();
+ method public long getNumPadAdd();
+ method public long getNumPadComma();
+ method public long getNumPadDivide();
+ method public long getNumPadDot();
+ method public long getNumPadEnter();
+ method public long getNumPadEquals();
+ method public long getNumPadLeftParenthesis();
+ method public long getNumPadMultiply();
+ method public long getNumPadRightParenthesis();
+ method public long getNumPadSubtract();
+ method public long getNumber();
+ method public long getO();
+ method public long getOne();
+ method public long getP();
+ method public long getPageDown();
+ method public long getPageUp();
+ method public long getPairing();
+ method public long getPaste();
+ method public long getPeriod();
+ method public long getPictureSymbols();
+ method public long getPlus();
+ method public long getPound();
+ method public long getPower();
+ method public long getPrintScreen();
+ method public long getProfileSwitch();
+ method public long getProgramBlue();
+ method public long getProgramGreen();
+ method public long getProgramRed();
+ method public long getProgramYellow();
+ method public long getQ();
+ method public long getR();
+ method public long getRefresh();
+ method public long getRightBracket();
+ method public long getRo();
+ method public long getS();
+ method public long getScrollLock();
+ method public long getSearch();
+ method public long getSemicolon();
+ method public long getSetTopBoxInput();
+ method public long getSetTopBoxPower();
+ method public long getSettings();
+ method public long getSeven();
+ method public long getShiftLeft();
+ method public long getShiftRight();
+ method public long getSix();
+ method public long getSlash();
+ method public long getSleep();
+ method public long getSoftLeft();
+ method public long getSoftRight();
+ method public long getSoftSleep();
+ method public long getSpacebar();
+ method public long getStem1();
+ method public long getStem2();
+ method public long getStem3();
+ method public long getStemPrimary();
+ method public long getSwitchCharset();
+ method public long getSymbol();
+ method public long getSystemNavigationDown();
+ method public long getSystemNavigationLeft();
+ method public long getSystemNavigationRight();
+ method public long getSystemNavigationUp();
+ method public long getT();
+ method public long getTab();
+ method public long getThree();
+ method public long getThumbsDown();
+ method public long getThumbsUp();
+ method public long getToggle2D3D();
+ method public long getTv();
+ method public long getTvAntennaCable();
+ method public long getTvAudioDescription();
+ method public long getTvAudioDescriptionMixingVolumeDown();
+ method public long getTvAudioDescriptionMixingVolumeUp();
+ method public long getTvContentsMenu();
+ method public long getTvDataService();
+ method public long getTvInput();
+ method public long getTvInputComponent1();
+ method public long getTvInputComponent2();
+ method public long getTvInputComposite1();
+ method public long getTvInputComposite2();
+ method public long getTvInputHdmi1();
+ method public long getTvInputHdmi2();
+ method public long getTvInputHdmi3();
+ method public long getTvInputHdmi4();
+ method public long getTvInputVga1();
+ method public long getTvMediaContextMenu();
+ method public long getTvNetwork();
+ method public long getTvNumberEntry();
+ method public long getTvPower();
+ method public long getTvRadioService();
+ method public long getTvSatellite();
+ method public long getTvSatelliteBs();
+ method public long getTvSatelliteCs();
+ method public long getTvSatelliteService();
+ method public long getTvTeletext();
+ method public long getTvTerrestrialAnalog();
+ method public long getTvTerrestrialDigital();
+ method public long getTvTimerProgramming();
+ method public long getTvZoomMode();
+ method public long getTwo();
+ method public long getU();
+ method public long getUnknown();
+ method public long getV();
+ method public long getVoiceAssist();
+ method public long getVolumeDown();
+ method public long getVolumeMute();
+ method public long getVolumeUp();
+ method public long getW();
+ method public long getWakeUp();
+ method public long getWindow();
+ method public long getX();
+ method public long getY();
+ method public long getYen();
+ method public long getZ();
+ method public long getZenkakuHankaru();
+ method public long getZero();
+ method public long getZoomIn();
+ method public long getZoomOut();
+ property public final long A;
+ property public final long AllApps;
+ property public final long AltLeft;
+ property public final long AltRight;
+ property public final long Apostrophe;
+ property public final long AppSwitch;
+ property public final long Assist;
+ property public final long At;
+ property public final long AvReceiverInput;
+ property public final long AvReceiverPower;
+ property public final long B;
+ property public final long Back;
+ property public final long Backslash;
+ property public final long Backspace;
+ property public final long Bookmark;
+ property public final long Break;
+ property public final long BrightnessDown;
+ property public final long BrightnessUp;
+ property public final long Browser;
+ property public final long Button1;
+ property public final long Button10;
+ property public final long Button11;
+ property public final long Button12;
+ property public final long Button13;
+ property public final long Button14;
+ property public final long Button15;
+ property public final long Button16;
+ property public final long Button2;
+ property public final long Button3;
+ property public final long Button4;
+ property public final long Button5;
+ property public final long Button6;
+ property public final long Button7;
+ property public final long Button8;
+ property public final long Button9;
+ property public final long ButtonA;
+ property public final long ButtonB;
+ property public final long ButtonC;
+ property public final long ButtonL1;
+ property public final long ButtonL2;
+ property public final long ButtonMode;
+ property public final long ButtonR1;
+ property public final long ButtonR2;
+ property public final long ButtonSelect;
+ property public final long ButtonStart;
+ property public final long ButtonThumbLeft;
+ property public final long ButtonThumbRight;
+ property public final long ButtonX;
+ property public final long ButtonY;
+ property public final long ButtonZ;
+ property public final long C;
+ property public final long Calculator;
+ property public final long Calendar;
+ property public final long Call;
+ property public final long Camera;
+ property public final long CapsLock;
+ property public final long Captions;
+ property public final long ChannelDown;
+ property public final long ChannelUp;
+ property public final long Clear;
+ property public final long Comma;
+ property public final long Contacts;
+ property public final long Copy;
+ property public final long CtrlLeft;
+ property public final long CtrlRight;
+ property public final long Cut;
+ property public final long D;
+ property public final long Delete;
+ property public final long DirectionCenter;
+ property public final long DirectionDown;
+ property public final long DirectionDownLeft;
+ property public final long DirectionDownRight;
+ property public final long DirectionLeft;
+ property public final long DirectionRight;
+ property public final long DirectionUp;
+ property public final long DirectionUpLeft;
+ property public final long DirectionUpRight;
+ property public final long Dvr;
+ property public final long E;
+ property public final long Eight;
+ property public final long Eisu;
+ property public final long EndCall;
+ property public final long Enter;
+ property public final long Envelope;
+ property public final long Equals;
+ property public final long Escape;
+ property public final long F;
+ property public final long F1;
+ property public final long F10;
+ property public final long F11;
+ property public final long F12;
+ property public final long F2;
+ property public final long F3;
+ property public final long F4;
+ property public final long F5;
+ property public final long F6;
+ property public final long F7;
+ property public final long F8;
+ property public final long F9;
+ property public final long Five;
+ property public final long Focus;
+ property public final long Forward;
+ property public final long Four;
+ property public final long Function;
+ property public final long G;
+ property public final long Grave;
+ property public final long Guide;
+ property public final long H;
+ property public final long HeadsetHook;
+ property public final long Help;
+ property public final long Henkan;
+ property public final long Home;
+ property public final long I;
+ property public final long Info;
+ property public final long Insert;
+ property public final long J;
+ property public final long K;
+ property public final long Kana;
+ property public final long KatakanaHiragana;
+ property public final long L;
+ property public final long LanguageSwitch;
+ property public final long LastChannel;
+ property public final long LeftBracket;
+ property public final long M;
+ property public final long MannerMode;
+ property public final long MediaAudioTrack;
+ property public final long MediaClose;
+ property public final long MediaEject;
+ property public final long MediaFastForward;
+ property public final long MediaNext;
+ property public final long MediaPause;
+ property public final long MediaPlay;
+ property public final long MediaPlayPause;
+ property public final long MediaPrevious;
+ property public final long MediaRecord;
+ property public final long MediaRewind;
+ property public final long MediaSkipBackward;
+ property public final long MediaSkipForward;
+ property public final long MediaStepBackward;
+ property public final long MediaStepForward;
+ property public final long MediaStop;
+ property public final long MediaTopMenu;
+ property public final long Menu;
+ property public final long MetaLeft;
+ property public final long MetaRight;
+ property public final long MicrophoneMute;
+ property public final long Minus;
+ property public final long MoveEnd;
+ property public final long MoveHome;
+ property public final long Muhenkan;
+ property public final long Multiply;
+ property public final long Music;
+ property public final long N;
+ property public final long NavigateIn;
+ property public final long NavigateNext;
+ property public final long NavigateOut;
+ property public final long NavigatePrevious;
+ property public final long Nine;
+ property public final long Notification;
+ property public final long NumLock;
+ property public final long NumPad0;
+ property public final long NumPad1;
+ property public final long NumPad2;
+ property public final long NumPad3;
+ property public final long NumPad4;
+ property public final long NumPad5;
+ property public final long NumPad6;
+ property public final long NumPad7;
+ property public final long NumPad8;
+ property public final long NumPad9;
+ property public final long NumPadAdd;
+ property public final long NumPadComma;
+ property public final long NumPadDivide;
+ property public final long NumPadDot;
+ property public final long NumPadEnter;
+ property public final long NumPadEquals;
+ property public final long NumPadLeftParenthesis;
+ property public final long NumPadMultiply;
+ property public final long NumPadRightParenthesis;
+ property public final long NumPadSubtract;
+ property public final long Number;
+ property public final long O;
+ property public final long One;
+ property public final long P;
+ property public final long PageDown;
+ property public final long PageUp;
+ property public final long Pairing;
+ property public final long Paste;
+ property public final long Period;
+ property public final long PictureSymbols;
+ property public final long Plus;
+ property public final long Pound;
+ property public final long Power;
+ property public final long PrintScreen;
+ property public final long ProfileSwitch;
+ property public final long ProgramBlue;
+ property public final long ProgramGreen;
+ property public final long ProgramRed;
+ property public final long ProgramYellow;
+ property public final long Q;
+ property public final long R;
+ property public final long Refresh;
+ property public final long RightBracket;
+ property public final long Ro;
+ property public final long S;
+ property public final long ScrollLock;
+ property public final long Search;
+ property public final long Semicolon;
+ property public final long SetTopBoxInput;
+ property public final long SetTopBoxPower;
+ property public final long Settings;
+ property public final long Seven;
+ property public final long ShiftLeft;
+ property public final long ShiftRight;
+ property public final long Six;
+ property public final long Slash;
+ property public final long Sleep;
+ property public final long SoftLeft;
+ property public final long SoftRight;
+ property public final long SoftSleep;
+ property public final long Spacebar;
+ property public final long Stem1;
+ property public final long Stem2;
+ property public final long Stem3;
+ property public final long StemPrimary;
+ property public final long SwitchCharset;
+ property public final long Symbol;
+ property public final long SystemNavigationDown;
+ property public final long SystemNavigationLeft;
+ property public final long SystemNavigationRight;
+ property public final long SystemNavigationUp;
+ property public final long T;
+ property public final long Tab;
+ property public final long Three;
+ property public final long ThumbsDown;
+ property public final long ThumbsUp;
+ property public final long Toggle2D3D;
+ property public final long Tv;
+ property public final long TvAntennaCable;
+ property public final long TvAudioDescription;
+ property public final long TvAudioDescriptionMixingVolumeDown;
+ property public final long TvAudioDescriptionMixingVolumeUp;
+ property public final long TvContentsMenu;
+ property public final long TvDataService;
+ property public final long TvInput;
+ property public final long TvInputComponent1;
+ property public final long TvInputComponent2;
+ property public final long TvInputComposite1;
+ property public final long TvInputComposite2;
+ property public final long TvInputHdmi1;
+ property public final long TvInputHdmi2;
+ property public final long TvInputHdmi3;
+ property public final long TvInputHdmi4;
+ property public final long TvInputVga1;
+ property public final long TvMediaContextMenu;
+ property public final long TvNetwork;
+ property public final long TvNumberEntry;
+ property public final long TvPower;
+ property public final long TvRadioService;
+ property public final long TvSatellite;
+ property public final long TvSatelliteBs;
+ property public final long TvSatelliteCs;
+ property public final long TvSatelliteService;
+ property public final long TvTeletext;
+ property public final long TvTerrestrialAnalog;
+ property public final long TvTerrestrialDigital;
+ property public final long TvTimerProgramming;
+ property public final long TvZoomMode;
+ property public final long Two;
+ property public final long U;
+ property public final long Unknown;
+ property public final long V;
+ property public final long VoiceAssist;
+ property public final long VolumeDown;
+ property public final long VolumeMute;
+ property public final long VolumeUp;
+ property public final long W;
+ property public final long WakeUp;
+ property public final long Window;
+ property public final long X;
+ property public final long Y;
+ property public final long Yen;
+ property public final long Z;
+ property public final long ZenkakuHankaru;
+ property public final long Zero;
+ property public final long ZoomIn;
+ property public final long ZoomOut;
+ }
+
+ @kotlin.jvm.JvmInline public final value class KeyEvent {
+ ctor public KeyEvent(android.view.KeyEvent nativeKeyEvent);
+ method public android.view.KeyEvent getNativeKeyEvent();
+ property public final android.view.KeyEvent nativeKeyEvent;
+ }
+
+ @kotlin.jvm.JvmInline public final value class KeyEventType {
+ field public static final androidx.compose.ui.input.key.KeyEventType.Companion Companion;
+ }
+
+ public static final class KeyEventType.Companion {
+ method public int getKeyDown();
+ method public int getKeyUp();
+ method public int getUnknown();
+ property public final int KeyDown;
+ property public final int KeyUp;
+ property public final int Unknown;
+ }
+
+ public final class KeyEvent_androidKt {
+ method public static long getKey(android.view.KeyEvent);
+ method public static int getType(android.view.KeyEvent);
+ method public static int getUtf16CodePoint(android.view.KeyEvent);
+ method public static boolean isAltPressed(android.view.KeyEvent);
+ method public static boolean isCtrlPressed(android.view.KeyEvent);
+ method public static boolean isMetaPressed(android.view.KeyEvent);
+ method public static boolean isShiftPressed(android.view.KeyEvent);
+ }
+
+ public final class KeyInputModifierKt {
+ method public static androidx.compose.ui.Modifier onKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onKeyEvent);
+ method public static androidx.compose.ui.Modifier onPreviewKeyEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreviewKeyEvent);
+ }
+
+ public interface KeyInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public boolean onKeyEvent(android.view.KeyEvent event);
+ method public boolean onPreKeyEvent(android.view.KeyEvent event);
+ }
+
+ public final class Key_androidKt {
+ method public static long Key(int nativeKeyCode);
+ method public static int getNativeKeyCode(long);
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftKeyboardInterceptionModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public boolean onInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
+ method public boolean onPreInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
+ }
+
+ public final class SoftwareKeyboardInterceptionModifierKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onInterceptKeyBeforeSoftKeyboard);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onPreInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreInterceptKeyBeforeSoftKeyboard);
+ }
+
+}
+
+package androidx.compose.ui.input.nestedscroll {
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface NestedScrollConnection {
+ method public default suspend Object? onPostFling(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>);
+ method public default long onPostScroll(long consumed, long available, int source);
+ method public default suspend Object? onPreFling(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>);
+ method public default long onPreScroll(long available, int source);
+ }
+
+ public final class NestedScrollDispatcher {
+ ctor public NestedScrollDispatcher();
+ method public suspend Object? dispatchPostFling(long consumed, long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>);
+ method public long dispatchPostScroll(long consumed, long available, int source);
+ method public suspend Object? dispatchPreFling(long available, kotlin.coroutines.Continuation<? super androidx.compose.ui.unit.Velocity>);
+ method public long dispatchPreScroll(long available, int source);
+ method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
+ property public final kotlinx.coroutines.CoroutineScope coroutineScope;
+ }
+
+ public final class NestedScrollModifierKt {
+ method public static androidx.compose.ui.Modifier nestedScroll(androidx.compose.ui.Modifier, androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, optional androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
+ }
+
+ public final class NestedScrollNodeKt {
+ method public static androidx.compose.ui.node.DelegatableNode nestedScrollModifierNode(androidx.compose.ui.input.nestedscroll.NestedScrollConnection connection, androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher? dispatcher);
+ }
+
+ @kotlin.jvm.JvmInline public final value class NestedScrollSource {
+ field public static final androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion Companion;
+ }
+
+ public static final class NestedScrollSource.Companion {
+ method public int getDrag();
+ method public int getFling();
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getRelocate();
+ property public final int Drag;
+ property public final int Fling;
+ property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Relocate;
+ }
+
+}
+
+package androidx.compose.ui.input.pointer {
+
+ @kotlin.coroutines.RestrictsSuspension @kotlin.jvm.JvmDefaultWithCompatibility public interface AwaitPointerEventScope extends androidx.compose.ui.unit.Density {
+ method public suspend Object? awaitPointerEvent(optional androidx.compose.ui.input.pointer.PointerEventPass pass, optional kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerEvent>);
+ method public androidx.compose.ui.input.pointer.PointerEvent getCurrentEvent();
+ method public default long getExtendedTouchPadding();
+ method public long getSize();
+ method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+ method public default suspend <T> Object? withTimeout(long timeMillis, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method public default suspend <T> Object? withTimeoutOrNull(long timeMillis, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ property public abstract androidx.compose.ui.input.pointer.PointerEvent currentEvent;
+ property public default long extendedTouchPadding;
+ property public abstract long size;
+ property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+ }
+
+ @Deprecated public final class ConsumedData {
+ ctor @Deprecated public ConsumedData(optional @Deprecated boolean positionChange, optional @Deprecated boolean downChange);
+ method @Deprecated public boolean getDownChange();
+ method @Deprecated public boolean getPositionChange();
+ method @Deprecated public void setDownChange(boolean);
+ method @Deprecated public void setPositionChange(boolean);
+ property @Deprecated public final boolean downChange;
+ property @Deprecated public final boolean positionChange;
+ }
+
+ @androidx.compose.runtime.Immutable @androidx.compose.ui.ExperimentalComposeUiApi public final class HistoricalChange {
+ ctor public HistoricalChange(long uptimeMillis, long position);
+ method public long getPosition();
+ method public long getUptimeMillis();
+ property public final long position;
+ property public final long uptimeMillis;
+ }
+
+ @kotlin.jvm.JvmInline public final value class PointerButtons {
+ ctor public PointerButtons(int packedValue);
+ }
+
+ public final class PointerEvent {
+ ctor public PointerEvent(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes);
+ method public java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> component1();
+ method public androidx.compose.ui.input.pointer.PointerEvent copy(java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes, android.view.MotionEvent? motionEvent);
+ method public int getButtons();
+ method public java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> getChanges();
+ method public int getKeyboardModifiers();
+ method public int getType();
+ property public final int buttons;
+ property public final java.util.List<androidx.compose.ui.input.pointer.PointerInputChange> changes;
+ property public final int keyboardModifiers;
+ property public final int type;
+ }
+
+ public final class PointerEventKt {
+ method @Deprecated public static boolean anyChangeConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean changedToDown(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean changedToDownIgnoreConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean changedToUp(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean changedToUpIgnoreConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ method @Deprecated public static void consumeAllChanges(androidx.compose.ui.input.pointer.PointerInputChange);
+ method @Deprecated public static void consumeDownChange(androidx.compose.ui.input.pointer.PointerInputChange);
+ method @Deprecated public static void consumePositionChange(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static int indexOfFirstPressed(int);
+ method public static int indexOfLastPressed(int);
+ method @Deprecated public static boolean isOutOfBounds(androidx.compose.ui.input.pointer.PointerInputChange, long size);
+ method public static boolean isOutOfBounds(androidx.compose.ui.input.pointer.PointerInputChange, long size, long extendedTouchPadding);
+ method public static boolean isPressed(int, int buttonIndex);
+ method public static long positionChange(androidx.compose.ui.input.pointer.PointerInputChange);
+ method @Deprecated public static boolean positionChangeConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static long positionChangeIgnoreConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean positionChanged(androidx.compose.ui.input.pointer.PointerInputChange);
+ method public static boolean positionChangedIgnoreConsumed(androidx.compose.ui.input.pointer.PointerInputChange);
+ }
+
+ public enum PointerEventPass {
+ method public static androidx.compose.ui.input.pointer.PointerEventPass valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.input.pointer.PointerEventPass[] values();
+ enum_constant public static final androidx.compose.ui.input.pointer.PointerEventPass Final;
+ enum_constant public static final androidx.compose.ui.input.pointer.PointerEventPass Initial;
+ enum_constant public static final androidx.compose.ui.input.pointer.PointerEventPass Main;
+ }
+
+ public final class PointerEventTimeoutCancellationException extends java.util.concurrent.CancellationException {
+ ctor public PointerEventTimeoutCancellationException(long time);
+ }
+
+ @kotlin.jvm.JvmInline public final value class PointerEventType {
+ field public static final androidx.compose.ui.input.pointer.PointerEventType.Companion Companion;
+ }
+
+ public static final class PointerEventType.Companion {
+ method public int getEnter();
+ method public int getExit();
+ method public int getMove();
+ method public int getPress();
+ method public int getRelease();
+ method 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 public final int Scroll;
+ property public final int Unknown;
+ }
+
+ public final class PointerEvent_androidKt {
+ method public static boolean getAreAnyPressed(int);
+ method public static int indexOfFirstPressed(int);
+ method public static int indexOfLastPressed(int);
+ method public static boolean isAltGraphPressed(int);
+ method public static boolean isAltPressed(int);
+ method public static boolean isBackPressed(int);
+ method public static boolean isCapsLockOn(int);
+ method public static boolean isCtrlPressed(int);
+ method public static boolean isForwardPressed(int);
+ method public static boolean isFunctionPressed(int);
+ method public static boolean isMetaPressed(int);
+ method public static boolean isNumLockOn(int);
+ method public static boolean isPressed(int, int buttonIndex);
+ method public static boolean isPrimaryPressed(int);
+ method public static boolean isScrollLockOn(int);
+ method public static boolean isSecondaryPressed(int);
+ method public static boolean isShiftPressed(int);
+ method public static boolean isSymPressed(int);
+ method public static boolean isTertiaryPressed(int);
+ }
+
+ @androidx.compose.runtime.Stable public interface PointerIcon {
+ field public static final androidx.compose.ui.input.pointer.PointerIcon.Companion Companion;
+ }
+
+ public static final class PointerIcon.Companion {
+ method public androidx.compose.ui.input.pointer.PointerIcon getCrosshair();
+ method public androidx.compose.ui.input.pointer.PointerIcon getDefault();
+ method public androidx.compose.ui.input.pointer.PointerIcon getHand();
+ method public androidx.compose.ui.input.pointer.PointerIcon getText();
+ property public final androidx.compose.ui.input.pointer.PointerIcon Crosshair;
+ property public final androidx.compose.ui.input.pointer.PointerIcon Default;
+ property public final androidx.compose.ui.input.pointer.PointerIcon Hand;
+ property public final androidx.compose.ui.input.pointer.PointerIcon Text;
+ }
+
+ public final class PointerIconKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier pointerHoverIcon(androidx.compose.ui.Modifier, androidx.compose.ui.input.pointer.PointerIcon icon, optional boolean overrideDescendants);
+ }
+
+ public final class PointerIcon_androidKt {
+ method public static androidx.compose.ui.input.pointer.PointerIcon PointerIcon(android.view.PointerIcon pointerIcon);
+ method public static androidx.compose.ui.input.pointer.PointerIcon PointerIcon(int pointerIconType);
+ }
+
+ @kotlin.jvm.JvmInline public final value class PointerId {
+ ctor public PointerId(long value);
+ method public long getValue();
+ property public final long value;
+ }
+
+ @androidx.compose.runtime.Immutable public final class PointerInputChange {
+ ctor public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, float pressure, long previousUptimeMillis, long previousPosition, boolean previousPressed, boolean isInitiallyConsumed, optional int type, optional long scrollDelta);
+ ctor @Deprecated 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 public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, boolean isInitiallyConsumed, optional int type, optional long scrollDelta);
+ method public void consume();
+ 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 float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
+ method public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional long scrollDelta);
+ method @Deprecated 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 @Deprecated 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, androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type, optional long scrollDelta);
+ 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 int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
+ 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 int type, optional long scrollDelta);
+ method @Deprecated public androidx.compose.ui.input.pointer.ConsumedData getConsumed();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> getHistorical();
+ method public long getId();
+ method public long getPosition();
+ method public boolean getPressed();
+ method public float getPressure();
+ method public long getPreviousPosition();
+ method public boolean getPreviousPressed();
+ method public long getPreviousUptimeMillis();
+ method public long getScrollDelta();
+ method public int getType();
+ method public long getUptimeMillis();
+ method public boolean isConsumed();
+ property @Deprecated 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 public final boolean isConsumed;
+ property public final long position;
+ property public final boolean pressed;
+ property public final float pressure;
+ property public final long previousPosition;
+ property public final boolean previousPressed;
+ property public final long previousUptimeMillis;
+ property public final long scrollDelta;
+ property public final int type;
+ property public final long uptimeMillis;
+ }
+
+ public abstract class PointerInputFilter {
+ ctor public PointerInputFilter();
+ method public boolean getInterceptOutOfBoundsChildEvents();
+ method public boolean getShareWithSiblings();
+ method public final long getSize();
+ method public abstract void onCancel();
+ method public abstract void onPointerEvent(androidx.compose.ui.input.pointer.PointerEvent pointerEvent, androidx.compose.ui.input.pointer.PointerEventPass pass, long bounds);
+ property public boolean interceptOutOfBoundsChildEvents;
+ property public boolean shareWithSiblings;
+ property public final long size;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface PointerInputModifier extends androidx.compose.ui.Modifier.Element {
+ method public androidx.compose.ui.input.pointer.PointerInputFilter getPointerInputFilter();
+ property public abstract androidx.compose.ui.input.pointer.PointerInputFilter pointerInputFilter;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface PointerInputScope extends androidx.compose.ui.unit.Density {
+ method public suspend <R> Object? awaitPointerEventScope(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
+ method public default long getExtendedTouchPadding();
+ method public default boolean getInterceptOutOfBoundsChildEvents();
+ method public long getSize();
+ method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+ method public default void setInterceptOutOfBoundsChildEvents(boolean);
+ property public default long extendedTouchPadding;
+ property public default boolean interceptOutOfBoundsChildEvents;
+ property public abstract long size;
+ property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+ }
+
+ public final class PointerInteropFilter_androidKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier motionEventSpy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,kotlin.Unit> watcher);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier pointerInteropFilter(androidx.compose.ui.Modifier, optional androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent? requestDisallowInterceptTouchEvent, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean> onTouchEvent);
+ }
+
+ @kotlin.jvm.JvmInline public final value class PointerKeyboardModifiers {
+ ctor public PointerKeyboardModifiers(int packedValue);
+ }
+
+ @kotlin.jvm.JvmInline public final value class PointerType {
+ field public static final androidx.compose.ui.input.pointer.PointerType.Companion Companion;
+ }
+
+ public static final class PointerType.Companion {
+ method public int getEraser();
+ method public int getMouse();
+ method public int getStylus();
+ method public int getTouch();
+ method public int getUnknown();
+ property public final int Eraser;
+ property public final int Mouse;
+ property public final int Stylus;
+ property public final int Touch;
+ property public final int Unknown;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public final class RequestDisallowInterceptTouchEvent implements kotlin.jvm.functions.Function1<java.lang.Boolean,kotlin.Unit> {
+ ctor public RequestDisallowInterceptTouchEvent();
+ method public void invoke(boolean disallowIntercept);
+ }
+
+ public final class SuspendingPointerInputFilterKt {
+ method public static androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode SuspendingPointerInputModifierNode(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> pointerInputHandler);
+ method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public sealed interface SuspendingPointerInputModifierNode extends androidx.compose.ui.node.PointerInputModifierNode {
+ method public kotlin.jvm.functions.Function2<androidx.compose.ui.input.pointer.PointerInputScope,kotlin.coroutines.Continuation<? super kotlin.Unit>,java.lang.Object> getPointerInputHandler();
+ method public void resetPointerInputHandler();
+ method public void setPointerInputHandler(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?>);
+ property public abstract kotlin.jvm.functions.Function2<androidx.compose.ui.input.pointer.PointerInputScope,kotlin.coroutines.Continuation<? super kotlin.Unit>,java.lang.Object> pointerInputHandler;
+ }
+
+}
+
+package androidx.compose.ui.input.pointer.util {
+
+ public final class VelocityTracker {
+ ctor public VelocityTracker();
+ method public void addPosition(long timeMillis, long position);
+ method public long calculateVelocity();
+ method public void resetTracking();
+ }
+
+ public final class VelocityTracker1D {
+ ctor public VelocityTracker1D(boolean isDataDifferential);
+ method public void addDataPoint(long timeMillis, float dataPoint);
+ method public float calculateVelocity();
+ method public boolean isDataDifferential();
+ method public void resetTracking();
+ property public final boolean isDataDifferential;
+ }
+
+ public final class VelocityTrackerKt {
+ method public static void addPointerInputChange(androidx.compose.ui.input.pointer.util.VelocityTracker, androidx.compose.ui.input.pointer.PointerInputChange event);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getVelocityTrackerAddPointsFix();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static void setVelocityTrackerAddPointsFix(boolean);
+ property @androidx.compose.ui.ExperimentalComposeUiApi public static final boolean VelocityTrackerAddPointsFix;
+ }
+
+}
+
+package androidx.compose.ui.input.rotary {
+
+ public final class RotaryInputModifierKt {
+ method public static androidx.compose.ui.Modifier onPreRotaryScrollEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.rotary.RotaryScrollEvent,java.lang.Boolean> onPreRotaryScrollEvent);
+ method public static androidx.compose.ui.Modifier onRotaryScrollEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.rotary.RotaryScrollEvent,java.lang.Boolean> onRotaryScrollEvent);
+ }
+
+ public interface RotaryInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public boolean onPreRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+ method public boolean onRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+ }
+
+ public final class RotaryScrollEvent {
+ method public float getHorizontalScrollPixels();
+ method public long getUptimeMillis();
+ method public float getVerticalScrollPixels();
+ property public final float horizontalScrollPixels;
+ property public final long uptimeMillis;
+ property public final float verticalScrollPixels;
+ }
+
+}
+
+package androidx.compose.ui.layout {
+
+ @androidx.compose.runtime.Immutable public abstract sealed class AlignmentLine {
+ field public static final androidx.compose.ui.layout.AlignmentLine.Companion Companion;
+ field public static final int Unspecified = -2147483648; // 0x80000000
+ }
+
+ public static final class AlignmentLine.Companion {
+ }
+
+ public final class AlignmentLineKt {
+ method public static androidx.compose.ui.layout.HorizontalAlignmentLine getFirstBaseline();
+ method public static androidx.compose.ui.layout.HorizontalAlignmentLine getLastBaseline();
+ property public static final androidx.compose.ui.layout.HorizontalAlignmentLine FirstBaseline;
+ property public static final androidx.compose.ui.layout.HorizontalAlignmentLine LastBaseline;
+ }
+
+ public interface BeyondBoundsLayout {
+ method public <T> T? layout(int direction, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.BeyondBoundsLayout.BeyondBoundsScope,? extends T> block);
+ }
+
+ public static interface BeyondBoundsLayout.BeyondBoundsScope {
+ method public boolean getHasMoreContent();
+ property public abstract boolean hasMoreContent;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class BeyondBoundsLayout.LayoutDirection {
+ field public static final androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion Companion;
+ }
+
+ public static final class BeyondBoundsLayout.LayoutDirection.Companion {
+ method public int getAbove();
+ method public int getAfter();
+ method public int getBefore();
+ method public int getBelow();
+ method public int getLeft();
+ method public int getRight();
+ property public final int Above;
+ property public final int After;
+ property public final int Before;
+ property public final int Below;
+ property public final int Left;
+ property public final int Right;
+ }
+
+ public final class BeyondBoundsLayoutKt {
+ method public static androidx.compose.ui.modifier.ProvidableModifierLocal<androidx.compose.ui.layout.BeyondBoundsLayout> getModifierLocalBeyondBoundsLayout();
+ property public static final androidx.compose.ui.modifier.ProvidableModifierLocal<androidx.compose.ui.layout.BeyondBoundsLayout> ModifierLocalBeyondBoundsLayout;
+ }
+
+ @androidx.compose.runtime.Stable public interface ContentScale {
+ method public long computeScaleFactor(long srcSize, long dstSize);
+ field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
+ }
+
+ public static final class ContentScale.Companion {
+ method public androidx.compose.ui.layout.ContentScale getCrop();
+ method public androidx.compose.ui.layout.ContentScale getFillBounds();
+ method public androidx.compose.ui.layout.ContentScale getFillHeight();
+ method public androidx.compose.ui.layout.ContentScale getFillWidth();
+ method public androidx.compose.ui.layout.ContentScale getFit();
+ method public androidx.compose.ui.layout.ContentScale getInside();
+ method public androidx.compose.ui.layout.FixedScale getNone();
+ property public final androidx.compose.ui.layout.ContentScale Crop;
+ property public final androidx.compose.ui.layout.ContentScale FillBounds;
+ property public final androidx.compose.ui.layout.ContentScale FillHeight;
+ property public final androidx.compose.ui.layout.ContentScale FillWidth;
+ property public final androidx.compose.ui.layout.ContentScale Fit;
+ property public final androidx.compose.ui.layout.ContentScale Inside;
+ property public final androidx.compose.ui.layout.FixedScale None;
+ }
+
+ @androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
+ ctor public FixedScale(float value);
+ method public float component1();
+ method public long computeScaleFactor(long srcSize, long dstSize);
+ method public androidx.compose.ui.layout.FixedScale copy(float value);
+ method public float getValue();
+ property public final float value;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface GraphicLayerInfo {
+ method public long getLayerId();
+ method public default long getOwnerViewId();
+ property public abstract long layerId;
+ property public default long ownerViewId;
+ }
+
+ public final class HorizontalAlignmentLine extends androidx.compose.ui.layout.AlignmentLine {
+ ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface IntermediateMeasureScope extends androidx.compose.ui.layout.LookaheadScope kotlinx.coroutines.CoroutineScope androidx.compose.ui.layout.MeasureScope {
+ method public long getLookaheadSize();
+ property public abstract long lookaheadSize;
+ }
+
+ public interface IntrinsicMeasurable {
+ method public Object? getParentData();
+ method public int maxIntrinsicHeight(int width);
+ method public int maxIntrinsicWidth(int height);
+ method public int minIntrinsicHeight(int width);
+ method public int minIntrinsicWidth(int height);
+ property public abstract Object? parentData;
+ }
+
+ public interface IntrinsicMeasureScope extends androidx.compose.ui.unit.Density {
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead();
+ property @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead;
+ property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutCoordinates {
+ method public operator int get(androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public androidx.compose.ui.layout.LayoutCoordinates? getParentCoordinates();
+ method public androidx.compose.ui.layout.LayoutCoordinates? getParentLayoutCoordinates();
+ method public java.util.Set<androidx.compose.ui.layout.AlignmentLine> getProvidedAlignmentLines();
+ method public long getSize();
+ method public boolean isAttached();
+ method public androidx.compose.ui.geometry.Rect localBoundingBoxOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional boolean clipBounds);
+ method public long localPositionOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource);
+ method public long localToRoot(long relativeToLocal);
+ method public long localToWindow(long relativeToLocal);
+ method public default void transformFrom(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, float[] matrix);
+ method public long windowToLocal(long relativeToWindow);
+ property public abstract boolean isAttached;
+ property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentCoordinates;
+ property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentLayoutCoordinates;
+ property public abstract java.util.Set<androidx.compose.ui.layout.AlignmentLine> providedAlignmentLines;
+ property public abstract long size;
+ }
+
+ public final class LayoutCoordinatesKt {
+ method public static androidx.compose.ui.geometry.Rect boundsInParent(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static androidx.compose.ui.geometry.Rect boundsInRoot(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static androidx.compose.ui.geometry.Rect boundsInWindow(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static androidx.compose.ui.layout.LayoutCoordinates findRootCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static long positionInParent(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static long positionInRoot(androidx.compose.ui.layout.LayoutCoordinates);
+ method public static long positionInWindow(androidx.compose.ui.layout.LayoutCoordinates);
+ }
+
+ public final class LayoutIdKt {
+ method public static Object? getLayoutId(androidx.compose.ui.layout.Measurable);
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier layoutId(androidx.compose.ui.Modifier, Object layoutId);
+ }
+
+ public interface LayoutIdParentData {
+ method public Object getLayoutId();
+ property public abstract Object layoutId;
+ }
+
+ public interface LayoutInfo {
+ method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public int getHeight();
+ method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
+ method public java.util.List<androidx.compose.ui.layout.ModifierInfo> getModifierInfo();
+ method public androidx.compose.ui.layout.LayoutInfo? getParentInfo();
+ method public int getSemanticsId();
+ method public androidx.compose.ui.platform.ViewConfiguration getViewConfiguration();
+ method public int getWidth();
+ method public boolean isAttached();
+ method public boolean isPlaced();
+ property public abstract androidx.compose.ui.layout.LayoutCoordinates coordinates;
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract int height;
+ property public abstract boolean isAttached;
+ property public abstract boolean isPlaced;
+ property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
+ property public abstract androidx.compose.ui.layout.LayoutInfo? parentInfo;
+ property public abstract int semanticsId;
+ property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
+ property public abstract int width;
+ }
+
+ public final class LayoutKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static inline void Layout(optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static inline void Layout(java.util.List<? extends kotlin.jvm.functions.Function0<kotlin.Unit>> contents, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MultiContentMeasurePolicy measurePolicy);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static inline void Layout(kotlin.jvm.functions.Function0<kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void MultiMeasureLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutModifier extends androidx.compose.ui.Modifier.Element {
+ method public default int maxIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int width);
+ method public default int maxIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int height);
+ method public androidx.compose.ui.layout.MeasureResult measure(androidx.compose.ui.layout.MeasureScope, androidx.compose.ui.layout.Measurable measurable, long constraints);
+ method public default int minIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int width);
+ method public default int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int height);
+ }
+
+ public final class LayoutModifierKt {
+ method public static androidx.compose.ui.Modifier layout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
+ }
+
+ @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface LookaheadLayoutCoordinates extends androidx.compose.ui.layout.LayoutCoordinates {
+ }
+
+ @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadLayoutScope {
+ method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadScope {
+ method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
+ method @Deprecated public default androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.ui.layout.MeasureResult> measure);
+ method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
+ method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ }
+
+ public final class LookaheadScopeKt {
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadLayout(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntermediateMeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
+ }
+
+ public interface Measurable extends androidx.compose.ui.layout.IntrinsicMeasurable {
+ method public androidx.compose.ui.layout.Placeable measure(long constraints);
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public fun interface MeasurePolicy {
+ method public default int maxIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int width);
+ method public default int maxIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int height);
+ method public androidx.compose.ui.layout.MeasureResult measure(androidx.compose.ui.layout.MeasureScope, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables, long constraints);
+ method public default int minIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int width);
+ method public default int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable> measurables, int height);
+ }
+
+ public interface MeasureResult {
+ method public java.util.Map<androidx.compose.ui.layout.AlignmentLine,java.lang.Integer> getAlignmentLines();
+ method public int getHeight();
+ method public int getWidth();
+ method public void placeChildren();
+ property public abstract java.util.Map<androidx.compose.ui.layout.AlignmentLine,java.lang.Integer> alignmentLines;
+ property public abstract int height;
+ property public abstract int width;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface MeasureScope extends androidx.compose.ui.layout.IntrinsicMeasureScope {
+ method public default androidx.compose.ui.layout.MeasureResult layout(int width, int height, optional java.util.Map<androidx.compose.ui.layout.AlignmentLine,java.lang.Integer> alignmentLines, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Placeable.PlacementScope,kotlin.Unit> placementBlock);
+ }
+
+ public interface Measured {
+ method public operator int get(androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public int getMeasuredHeight();
+ method public int getMeasuredWidth();
+ method public default Object? getParentData();
+ property public abstract int measuredHeight;
+ property public abstract int measuredWidth;
+ property public default Object? parentData;
+ }
+
+ public final class ModifierInfo {
+ ctor public ModifierInfo(androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.LayoutCoordinates coordinates, optional Object? extra);
+ method public androidx.compose.ui.layout.LayoutCoordinates getCoordinates();
+ method public Object? getExtra();
+ method public androidx.compose.ui.Modifier getModifier();
+ property public final androidx.compose.ui.layout.LayoutCoordinates coordinates;
+ property public final Object? extra;
+ property public final androidx.compose.ui.Modifier modifier;
+ }
+
+ @androidx.compose.runtime.Stable public fun interface MultiContentMeasurePolicy {
+ method public default int maxIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>> measurables, int width);
+ method public default int maxIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>> measurables, int height);
+ method public androidx.compose.ui.layout.MeasureResult measure(androidx.compose.ui.layout.MeasureScope, java.util.List<? extends java.util.List<? extends androidx.compose.ui.layout.Measurable>> measurables, long constraints);
+ method public default int minIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>> measurables, int width);
+ method public default int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, java.util.List<? extends java.util.List<? extends androidx.compose.ui.layout.IntrinsicMeasurable>> measurables, int height);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface OnGloballyPositionedModifier extends androidx.compose.ui.Modifier.Element {
+ method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ }
+
+ public final class OnGloballyPositionedModifierKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onGloballyPositioned(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,kotlin.Unit> onGloballyPositioned);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface OnPlacedModifier extends androidx.compose.ui.Modifier.Element {
+ method public void onPlaced(androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ }
+
+ public final class OnPlacedModifierKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LayoutCoordinates,kotlin.Unit> onPlaced);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface OnRemeasuredModifier extends androidx.compose.ui.Modifier.Element {
+ method public void onRemeasured(long size);
+ }
+
+ public final class OnRemeasuredModifierKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier onSizeChanged(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.IntSize,kotlin.Unit> onSizeChanged);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ParentDataModifier extends androidx.compose.ui.Modifier.Element {
+ method public Object? modifyParentData(androidx.compose.ui.unit.Density, Object? parentData);
+ }
+
+ @androidx.compose.runtime.Stable public interface PinnableContainer {
+ method public androidx.compose.ui.layout.PinnableContainer.PinnedHandle pin();
+ }
+
+ public static fun interface PinnableContainer.PinnedHandle {
+ method public void release();
+ }
+
+ public final class PinnableContainerKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.layout.PinnableContainer> getLocalPinnableContainer();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.layout.PinnableContainer> LocalPinnableContainer;
+ }
+
+ public abstract class Placeable implements androidx.compose.ui.layout.Measured {
+ ctor public Placeable();
+ method protected final long getApparentToRealOffset();
+ method public final int getHeight();
+ method public int getMeasuredHeight();
+ method protected final long getMeasuredSize();
+ method public int getMeasuredWidth();
+ method protected final long getMeasurementConstraints();
+ method public final int getWidth();
+ method protected abstract void placeAt(long position, float zIndex, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit>? layerBlock);
+ method protected final void setMeasuredSize(long);
+ method protected final void setMeasurementConstraints(long);
+ property protected final long apparentToRealOffset;
+ property public final int height;
+ property public int measuredHeight;
+ property protected final long measuredSize;
+ property public int measuredWidth;
+ property protected final long measurementConstraints;
+ property public final int width;
+ }
+
+ public abstract static class Placeable.PlacementScope {
+ ctor public Placeable.PlacementScope();
+ method public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
+ method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
+ method protected abstract int getParentWidth();
+ method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+ method public final void place(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
+ method public final void placeRelative(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
+ method public final void placeRelative(androidx.compose.ui.layout.Placeable, long position, optional float zIndex);
+ method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+ method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+ method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+ method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+ property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
+ property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
+ property protected abstract int parentWidth;
+ }
+
+ @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi @kotlin.jvm.JvmDefaultWithCompatibility public interface RelocationModifier extends androidx.compose.ui.Modifier.Element {
+ method @Deprecated public androidx.compose.ui.geometry.Rect computeDestination(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);
+ method @Deprecated public suspend Object? performRelocation(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.geometry.Rect destination, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class RelocationModifierKt {
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onRelocationRequest(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> onProvideDestination, kotlin.jvm.functions.Function3<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.geometry.Rect,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPerformRelocation);
+ }
+
+ @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class RelocationRequester {
+ ctor @Deprecated public RelocationRequester();
+ method @Deprecated public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class RelocationRequesterModifierKt {
+ method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier relocationRequester(androidx.compose.ui.Modifier, Object relocationRequester);
+ }
+
+ public interface Remeasurement {
+ method public void forceRemeasure();
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface RemeasurementModifier extends androidx.compose.ui.Modifier.Element {
+ method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class ScaleFactor {
+ method @androidx.compose.runtime.Stable public inline operator float component1();
+ method @androidx.compose.runtime.Stable public inline operator float component2();
+ method public long copy(optional float scaleX, optional float scaleY);
+ method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public float getScaleX();
+ method public float getScaleY();
+ method @androidx.compose.runtime.Stable public operator long times(float operand);
+ property @androidx.compose.runtime.Stable public final float scaleX;
+ property @androidx.compose.runtime.Stable public final float scaleY;
+ field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+ }
+
+ public static final class ScaleFactor.Companion {
+ method public long getUnspecified();
+ property public final long Unspecified;
+ }
+
+ public final class ScaleFactorKt {
+ method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div(long, long scaleFactor);
+ method public static inline boolean isSpecified(long);
+ method public static inline boolean isUnspecified(long);
+ method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
+ method public static inline long takeOrElse(long, kotlin.jvm.functions.Function0<androidx.compose.ui.layout.ScaleFactor> block);
+ method @androidx.compose.runtime.Stable public static operator long times(long, long scaleFactor);
+ method @androidx.compose.runtime.Stable public static operator long times(long, long size);
+ }
+
+ public final class SubcomposeLayoutKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void SubcomposeLayout(androidx.compose.ui.layout.SubcomposeLayoutState state, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method @androidx.compose.runtime.Composable public static void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method public static androidx.compose.ui.layout.SubcomposeSlotReusePolicy SubcomposeSlotReusePolicy(int maxSlotsToRetainForReuse);
+ }
+
+ public final class SubcomposeLayoutState {
+ ctor public SubcomposeLayoutState();
+ ctor public SubcomposeLayoutState(androidx.compose.ui.layout.SubcomposeSlotReusePolicy slotReusePolicy);
+ ctor @Deprecated public SubcomposeLayoutState(int maxSlotsToRetainForReuse);
+ method public androidx.compose.ui.layout.SubcomposeLayoutState.PrecomposedSlotHandle precompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public static interface SubcomposeLayoutState.PrecomposedSlotHandle {
+ method public void dispose();
+ method public default int getPlaceablesCount();
+ method public default void premeasure(int index, long constraints);
+ property public default int placeablesCount;
+ }
+
+ public interface SubcomposeMeasureScope extends androidx.compose.ui.layout.MeasureScope {
+ method public java.util.List<androidx.compose.ui.layout.Measurable> subcompose(Object? slotId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public interface SubcomposeSlotReusePolicy {
+ method public boolean areCompatible(Object? slotId, Object? reusableSlotId);
+ method public void getSlotsToRetain(androidx.compose.ui.layout.SubcomposeSlotReusePolicy.SlotIdsSet slotIds);
+ }
+
+ public static final class SubcomposeSlotReusePolicy.SlotIdsSet implements java.util.Collection<java.lang.Object> kotlin.jvm.internal.markers.KMappedMarker {
+ method public void clear();
+ method public java.util.Iterator<java.lang.Object> iterator();
+ method public boolean remove(Object? slotId);
+ method public boolean removeAll(java.util.Collection<?> slotIds);
+ method public boolean removeAll(kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> predicate);
+ method public boolean retainAll(java.util.Collection<?> slotIds);
+ method public boolean retainAll(kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Boolean> predicate);
+ }
+
+ public final class TestModifierUpdaterKt {
+ }
+
+ public final class VerticalAlignmentLine extends androidx.compose.ui.layout.AlignmentLine {
+ ctor public VerticalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
+ }
+
+}
+
+package androidx.compose.ui.modifier {
+
+ @androidx.compose.runtime.Stable public abstract sealed class ModifierLocal<T> {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface ModifierLocalConsumer extends androidx.compose.ui.Modifier.Element {
+ method public void onModifierLocalsUpdated(androidx.compose.ui.modifier.ModifierLocalReadScope scope);
+ }
+
+ public final class ModifierLocalConsumerKt {
+ method @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier modifierLocalConsumer(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.modifier.ModifierLocalReadScope,kotlin.Unit> consumer);
+ }
+
+ public final class ModifierLocalKt {
+ method public static <T> androidx.compose.ui.modifier.ProvidableModifierLocal<T> modifierLocalOf(kotlin.jvm.functions.Function0<? extends T> defaultFactory);
+ }
+
+ public abstract sealed class ModifierLocalMap {
+ }
+
+ public interface ModifierLocalModifierNode extends androidx.compose.ui.modifier.ModifierLocalReadScope androidx.compose.ui.node.DelegatableNode {
+ method public default <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
+ method public default androidx.compose.ui.modifier.ModifierLocalMap getProvidedValues();
+ method public default <T> void provide(androidx.compose.ui.modifier.ModifierLocal<T> key, T value);
+ property public default androidx.compose.ui.modifier.ModifierLocalMap providedValues;
+ }
+
+ public final class ModifierLocalModifierNodeKt {
+ method public static androidx.compose.ui.modifier.ModifierLocalMap modifierLocalMapOf();
+ method public static androidx.compose.ui.modifier.ModifierLocalMap modifierLocalMapOf(androidx.compose.ui.modifier.ModifierLocal<?>... keys);
+ method public static <T> androidx.compose.ui.modifier.ModifierLocalMap modifierLocalMapOf(androidx.compose.ui.modifier.ModifierLocal<T> key);
+ method public static androidx.compose.ui.modifier.ModifierLocalMap modifierLocalMapOf(kotlin.Pair<? extends androidx.compose.ui.modifier.ModifierLocal<?>,?>... entries);
+ method public static <T> androidx.compose.ui.modifier.ModifierLocalMap modifierLocalMapOf(kotlin.Pair<? extends androidx.compose.ui.modifier.ModifierLocal<T>,? extends T> entry);
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface ModifierLocalProvider<T> extends androidx.compose.ui.Modifier.Element {
+ method public androidx.compose.ui.modifier.ProvidableModifierLocal<T> getKey();
+ method public T getValue();
+ property public abstract androidx.compose.ui.modifier.ProvidableModifierLocal<T> key;
+ property public abstract T value;
+ }
+
+ public final class ModifierLocalProviderKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static <T> androidx.compose.ui.Modifier modifierLocalProvider(androidx.compose.ui.Modifier, androidx.compose.ui.modifier.ProvidableModifierLocal<T> key, kotlin.jvm.functions.Function0<? extends T> value);
+ }
+
+ public interface ModifierLocalReadScope {
+ method public <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
+ }
+
+ @androidx.compose.runtime.Stable public final class ProvidableModifierLocal<T> extends androidx.compose.ui.modifier.ModifierLocal<T> {
+ ctor public ProvidableModifierLocal(kotlin.jvm.functions.Function0<? extends T> defaultFactory);
+ }
+
+}
+
+package androidx.compose.ui.node {
+
+ public interface CacheDrawModifierNode extends androidx.compose.ui.node.DrawModifierNode {
+ method public void invalidateDrawCache();
+ }
+
+ public interface CompositionLocalConsumerModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ }
+
+ public final class CompositionLocalConsumerModifierNodeKt {
+ method public static <T> T currentValueOf(androidx.compose.ui.node.CompositionLocalConsumerModifierNode, androidx.compose.runtime.CompositionLocal<T> local);
+ }
+
+ public interface DelegatableNode {
+ method public androidx.compose.ui.Modifier.Node getNode();
+ property public abstract androidx.compose.ui.Modifier.Node node;
+ }
+
+ public final class DelegatableNodeKt {
+ method public static void invalidateSubtree(androidx.compose.ui.node.DelegatableNode);
+ method public static androidx.compose.ui.unit.Density requireDensity(androidx.compose.ui.node.DelegatableNode);
+ method public static androidx.compose.ui.unit.LayoutDirection requireLayoutDirection(androidx.compose.ui.node.DelegatableNode);
+ }
+
+ public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
+ ctor public DelegatingNode();
+ method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+ method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
+ }
+
+ public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void draw(androidx.compose.ui.graphics.drawscope.ContentDrawScope);
+ method public default void onMeasureResultChanged();
+ }
+
+ public final class DrawModifierNodeKt {
+ method public static void invalidateDraw(androidx.compose.ui.node.DrawModifierNode);
+ }
+
+ public interface GlobalPositionAwareModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ }
+
+ @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalCoreApi {
+ }
+
+ @androidx.compose.ui.InternalComposeUiApi public sealed interface InteroperableComposeUiNode {
+ method public android.view.View? getInteropView();
+ }
+
+ public interface LayoutAwareModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public default void onPlaced(androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ method public default void onRemeasured(long size);
+ }
+
+ public interface LayoutModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public default int maxIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int width);
+ method public default int maxIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int height);
+ method public androidx.compose.ui.layout.MeasureResult measure(androidx.compose.ui.layout.MeasureScope, androidx.compose.ui.layout.Measurable measurable, long constraints);
+ method public default int minIntrinsicHeight(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int width);
+ method public default int minIntrinsicWidth(androidx.compose.ui.layout.IntrinsicMeasureScope, androidx.compose.ui.layout.IntrinsicMeasurable measurable, int height);
+ }
+
+ public final class LayoutModifierNodeKt {
+ method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void remeasureSync(androidx.compose.ui.node.LayoutModifierNode);
+ }
+
+ public abstract class ModifierNodeElement<N extends androidx.compose.ui.Modifier.Node> implements androidx.compose.ui.platform.InspectableValue androidx.compose.ui.Modifier.Element {
+ ctor public ModifierNodeElement();
+ method public abstract N create();
+ method public abstract boolean equals(Object? other);
+ method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
+ method public final String? getNameFallback();
+ method public final Object? getValueOverride();
+ method public abstract int hashCode();
+ method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
+ method public abstract void update(N node);
+ property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
+ property public final String? nameFallback;
+ property public final Object? valueOverride;
+ }
+
+ public interface ObserverModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void onObservedReadsChanged();
+ }
+
+ public final class ObserverModifierNodeKt {
+ method public static <T extends androidx.compose.ui.Modifier.Node & androidx.compose.ui.node.ObserverModifierNode> void observeReads(T, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public interface ParentDataModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public Object? modifyParentData(androidx.compose.ui.unit.Density, Object? parentData);
+ }
+
+ public final class ParentDataModifierNodeKt {
+ method public static void invalidateParentData(androidx.compose.ui.node.ParentDataModifierNode);
+ }
+
+ public interface PointerInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public default boolean interceptOutOfBoundsChildEvents();
+ method public void onCancelPointerInput();
+ method public default void onDensityChange();
+ method public void onPointerEvent(androidx.compose.ui.input.pointer.PointerEvent pointerEvent, androidx.compose.ui.input.pointer.PointerEventPass pass, long bounds);
+ method public default void onViewConfigurationChange();
+ method public default boolean sharePointerInputWithSiblings();
+ }
+
+ public final class Ref<T> {
+ ctor public Ref();
+ method public T? getValue();
+ method public void setValue(T?);
+ property public final T? value;
+ }
+
+ public interface RootForTest {
+ method public androidx.compose.ui.unit.Density getDensity();
+ method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
+ method public androidx.compose.ui.text.input.TextInputService getTextInputService();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default void measureAndLayoutForTest();
+ method public boolean sendKeyEvent(android.view.KeyEvent keyEvent);
+ property public abstract androidx.compose.ui.unit.Density density;
+ property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
+ property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
+ }
+
+ public interface SemanticsModifierNode extends androidx.compose.ui.node.DelegatableNode {
+ method public void applySemantics(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public default boolean getShouldClearDescendantSemantics();
+ method public default boolean getShouldMergeDescendantSemantics();
+ property public default boolean shouldClearDescendantSemantics;
+ property public default boolean shouldMergeDescendantSemantics;
+ }
+
+ public final class SemanticsModifierNodeKt {
+ method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
+ }
+
+}
+
+package androidx.compose.ui.platform {
+
+ public abstract class AbstractComposeView extends android.view.ViewGroup {
+ ctor public AbstractComposeView(android.content.Context context);
+ ctor public AbstractComposeView(android.content.Context context, optional android.util.AttributeSet? attrs);
+ ctor public AbstractComposeView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public abstract void Content();
+ method public final void createComposition();
+ method public final void disposeComposition();
+ method public final boolean getHasComposition();
+ method protected boolean getShouldCreateCompositionOnAttachedToWindow();
+ method public final boolean getShowLayoutBounds();
+ method protected final void onLayout(boolean changed, int left, int top, int right, int bottom);
+ method protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
+ method public final void setParentCompositionContext(androidx.compose.runtime.CompositionContext? parent);
+ method public final void setShowLayoutBounds(boolean);
+ method public final void setViewCompositionStrategy(androidx.compose.ui.platform.ViewCompositionStrategy strategy);
+ property public final boolean hasComposition;
+ property protected boolean shouldCreateCompositionOnAttachedToWindow;
+ property public final boolean showLayoutBounds;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface AccessibilityManager {
+ method public long calculateRecommendedTimeoutMillis(long originalTimeoutMillis, optional boolean containsIcons, optional boolean containsText, optional boolean containsControls);
+ }
+
+ public final class AndroidCompositionLocals_androidKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.res.Configuration> getLocalConfiguration();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.lifecycle.LifecycleOwner> getLocalLifecycleOwner();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.savedstate.SavedStateRegistryOwner> getLocalSavedStateRegistryOwner();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.view.View> getLocalView();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.res.Configuration> LocalConfiguration;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.lifecycle.LifecycleOwner> LocalLifecycleOwner;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.savedstate.SavedStateRegistryOwner> LocalSavedStateRegistryOwner;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.view.View> LocalView;
+ }
+
+ public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
+ method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
+ method public android.view.Choreographer getChoreographer();
+ method public androidx.compose.runtime.MonotonicFrameClock getFrameClock();
+ property public final android.view.Choreographer choreographer;
+ property public final androidx.compose.runtime.MonotonicFrameClock frameClock;
+ field public static final androidx.compose.ui.platform.AndroidUiDispatcher.Companion Companion;
+ }
+
+ public static final class AndroidUiDispatcher.Companion {
+ method public kotlin.coroutines.CoroutineContext getCurrentThread();
+ method public kotlin.coroutines.CoroutineContext getMain();
+ property public final kotlin.coroutines.CoroutineContext CurrentThread;
+ property public final kotlin.coroutines.CoroutineContext Main;
+ }
+
+ public final class AndroidUiFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+ ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
+ method public android.view.Choreographer getChoreographer();
+ method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R>);
+ property public final android.view.Choreographer choreographer;
+ }
+
+ public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
+ ctor public AndroidUriHandler(android.content.Context context);
+ method public void openUri(String uri);
+ }
+
+ public final class AndroidViewConfiguration implements androidx.compose.ui.platform.ViewConfiguration {
+ ctor public AndroidViewConfiguration(android.view.ViewConfiguration viewConfiguration);
+ method public long getDoubleTapMinTimeMillis();
+ method public long getDoubleTapTimeoutMillis();
+ method public long getLongPressTimeoutMillis();
+ method public float getTouchSlop();
+ property public long doubleTapMinTimeMillis;
+ property public long doubleTapTimeoutMillis;
+ property public long longPressTimeoutMillis;
+ property public float touchSlop;
+ }
+
+ public interface ClipboardManager {
+ method public androidx.compose.ui.text.AnnotatedString? getText();
+ method public default boolean hasText();
+ method public void setText(androidx.compose.ui.text.AnnotatedString annotatedString);
+ }
+
+ public final class ComposeView extends androidx.compose.ui.platform.AbstractComposeView {
+ ctor public ComposeView(android.content.Context context);
+ ctor public ComposeView(android.content.Context context, optional android.util.AttributeSet? attrs);
+ ctor public ComposeView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+ method @androidx.compose.runtime.Composable public void Content();
+ method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ property protected boolean shouldCreateCompositionOnAttachedToWindow;
+ }
+
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> getLocalAutofill();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> getLocalDensity();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> getLocalFocusManager();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.font.FontFamily.Resolver> getLocalFontFamilyResolver();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> getLocalHapticFeedback();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> getLocalInputModeManager();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> getLocalLayoutDirection();
+ method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> getLocalPlatformTextInputPluginRegistry();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> getLocalTextInputService();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> getLocalTextToolbar();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> getLocalUriHandler();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> getLocalViewConfiguration();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> getLocalWindowInfo();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> LocalAccessibilityManager;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> LocalAutofill;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> LocalClipboardManager;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> LocalDensity;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> LocalFocusManager;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.font.FontFamily.Resolver> LocalFontFamilyResolver;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> LocalHapticFeedback;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> LocalInputModeManager;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> LocalLayoutDirection;
+ property @androidx.compose.ui.text.ExperimentalTextApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> LocalPlatformTextInputPluginRegistry;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> LocalTextInputService;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> LocalTextToolbar;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> LocalUriHandler;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> LocalViewConfiguration;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> LocalWindowInfo;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface InfiniteAnimationPolicy extends kotlin.coroutines.CoroutineContext.Element {
+ method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+ method public suspend <R> Object? onInfiniteOperation(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,?> block, kotlin.coroutines.Continuation<? super R>);
+ property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+ field public static final androidx.compose.ui.platform.InfiniteAnimationPolicy.Key Key;
+ }
+
+ public static final class InfiniteAnimationPolicy.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.platform.InfiniteAnimationPolicy> {
+ }
+
+ public final class InspectableModifier extends androidx.compose.ui.platform.InspectorValueInfo implements androidx.compose.ui.Modifier.Element {
+ ctor public InspectableModifier(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo);
+ method public androidx.compose.ui.platform.InspectableModifier.End getEnd();
+ property public final androidx.compose.ui.platform.InspectableModifier.End end;
+ }
+
+ public final class InspectableModifier.End implements androidx.compose.ui.Modifier.Element {
+ ctor public InspectableModifier.End();
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface InspectableValue {
+ method public default kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
+ method public default String? getNameFallback();
+ method public default Object? getValueOverride();
+ property public default kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
+ property public default String? nameFallback;
+ property public default Object? valueOverride;
+ }
+
+ public final class InspectableValueKt {
+ method public static inline kotlin.jvm.functions.Function1<androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> debugInspectorInfo(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> definitions);
+ method public static kotlin.jvm.functions.Function1<androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> getNoInspectorInfo();
+ method public static inline androidx.compose.ui.Modifier inspectable(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
+ method public static boolean isDebugInspectorInfoEnabled();
+ method public static void setDebugInspectorInfoEnabled(boolean);
+ property public static final kotlin.jvm.functions.Function1<androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> NoInspectorInfo;
+ property public static final boolean isDebugInspectorInfoEnabled;
+ }
+
+ public final class InspectionModeKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalInspectionMode();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalInspectionMode;
+ }
+
+ public final class InspectorInfo {
+ ctor public InspectorInfo();
+ method public String? getName();
+ method public androidx.compose.ui.platform.ValueElementSequence getProperties();
+ method public Object? getValue();
+ method public void setName(String?);
+ method public void setValue(Object?);
+ property public final String? name;
+ property public final androidx.compose.ui.platform.ValueElementSequence properties;
+ property public final Object? value;
+ }
+
+ public abstract class InspectorValueInfo implements androidx.compose.ui.platform.InspectableValue {
+ ctor public InspectorValueInfo(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> info);
+ property public kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
+ property public String? nameFallback;
+ property public Object? valueOverride;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public final class LocalSoftwareKeyboardController {
+ method @androidx.compose.runtime.Composable public androidx.compose.ui.platform.SoftwareKeyboardController? getCurrent();
+ method public infix androidx.compose.runtime.ProvidedValue<androidx.compose.ui.platform.SoftwareKeyboardController> provides(androidx.compose.ui.platform.SoftwareKeyboardController softwareKeyboardController);
+ property @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.platform.SoftwareKeyboardController? current;
+ field public static final androidx.compose.ui.platform.LocalSoftwareKeyboardController INSTANCE;
+ }
+
+ public final class NestedScrollInteropConnectionKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.input.nestedscroll.NestedScrollConnection rememberNestedScrollInteropConnection(optional android.view.View hostView);
+ }
+
+ @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftwareKeyboardController {
+ method public void hide();
+ method @Deprecated public default void hideSoftwareKeyboard();
+ method public void show();
+ method @Deprecated public default void showSoftwareKeyboard();
+ }
+
+ public final class TestTagKt {
+ method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface TextToolbar {
+ method public androidx.compose.ui.platform.TextToolbarStatus getStatus();
+ method public void hide();
+ method public void showMenu(androidx.compose.ui.geometry.Rect rect, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onCopyRequested, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onPasteRequested, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onCutRequested, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onSelectAllRequested);
+ property public abstract androidx.compose.ui.platform.TextToolbarStatus status;
+ }
+
+ public enum TextToolbarStatus {
+ method public static androidx.compose.ui.platform.TextToolbarStatus valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.platform.TextToolbarStatus[] values();
+ enum_constant public static final androidx.compose.ui.platform.TextToolbarStatus Hidden;
+ enum_constant public static final androidx.compose.ui.platform.TextToolbarStatus Shown;
+ }
+
+ public interface UriHandler {
+ method public void openUri(String uri);
+ }
+
+ public final class ValueElement {
+ ctor public ValueElement(String name, Object? value);
+ method public String component1();
+ method public Object? component2();
+ method public androidx.compose.ui.platform.ValueElement copy(String name, Object? value);
+ method public String getName();
+ method public Object? getValue();
+ property public final String name;
+ property public final Object? value;
+ }
+
+ public final class ValueElementSequence implements kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> {
+ ctor public ValueElementSequence();
+ method public java.util.Iterator<androidx.compose.ui.platform.ValueElement> iterator();
+ method public operator void set(String name, Object? value);
+ }
+
+ public interface ViewCompositionStrategy {
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+ field public static final androidx.compose.ui.platform.ViewCompositionStrategy.Companion Companion;
+ }
+
+ public static final class ViewCompositionStrategy.Companion {
+ method public androidx.compose.ui.platform.ViewCompositionStrategy getDefault();
+ property public final androidx.compose.ui.platform.ViewCompositionStrategy Default;
+ }
+
+ public static final class ViewCompositionStrategy.DisposeOnDetachedFromWindow implements androidx.compose.ui.platform.ViewCompositionStrategy {
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+ field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow INSTANCE;
+ }
+
+ public static final class ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool implements androidx.compose.ui.platform.ViewCompositionStrategy {
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+ field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool INSTANCE;
+ }
+
+ public static final class ViewCompositionStrategy.DisposeOnLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+ ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.Lifecycle lifecycle);
+ ctor public ViewCompositionStrategy.DisposeOnLifecycleDestroyed(androidx.lifecycle.LifecycleOwner lifecycleOwner);
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+ }
+
+ public static final class ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed implements androidx.compose.ui.platform.ViewCompositionStrategy {
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> installFor(androidx.compose.ui.platform.AbstractComposeView view);
+ field public static final androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed INSTANCE;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ViewConfiguration {
+ method public long getDoubleTapMinTimeMillis();
+ method public long getDoubleTapTimeoutMillis();
+ method public long getLongPressTimeoutMillis();
+ method public default long getMinimumTouchTargetSize();
+ method public float getTouchSlop();
+ property public abstract long doubleTapMinTimeMillis;
+ property public abstract long doubleTapTimeoutMillis;
+ property public abstract long longPressTimeoutMillis;
+ property public default long minimumTouchTargetSize;
+ property public abstract float touchSlop;
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface ViewRootForInspector {
+ method public default androidx.compose.ui.platform.AbstractComposeView? getSubCompositionView();
+ method public default android.view.View? getViewRoot();
+ property public default androidx.compose.ui.platform.AbstractComposeView? subCompositionView;
+ property public default android.view.View? viewRoot;
+ }
+
+ @VisibleForTesting public interface ViewRootForTest extends androidx.compose.ui.node.RootForTest {
+ method public boolean getHasPendingMeasureOrLayout();
+ method public android.view.View getView();
+ method public void invalidateDescendants();
+ method public boolean isLifecycleInResumedState();
+ property public abstract boolean hasPendingMeasureOrLayout;
+ property public abstract boolean isLifecycleInResumedState;
+ property public abstract android.view.View view;
+ field public static final androidx.compose.ui.platform.ViewRootForTest.Companion Companion;
+ }
+
+ public static final class ViewRootForTest.Companion {
+ method public kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? getOnViewCreatedCallback();
+ method public void setOnViewCreatedCallback(kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>?);
+ property public final kotlin.jvm.functions.Function1<androidx.compose.ui.platform.ViewRootForTest,kotlin.Unit>? onViewCreatedCallback;
+ }
+
+ @androidx.compose.runtime.Stable public interface WindowInfo {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public default int getKeyboardModifiers();
+ method public boolean isWindowFocused();
+ property public abstract boolean isWindowFocused;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public default int keyboardModifiers;
+ }
+
+ @androidx.compose.ui.InternalComposeUiApi public fun interface WindowRecomposerFactory {
+ method public androidx.compose.runtime.Recomposer createRecomposer(android.view.View windowRootView);
+ field public static final androidx.compose.ui.platform.WindowRecomposerFactory.Companion Companion;
+ }
+
+ public static final class WindowRecomposerFactory.Companion {
+ method public androidx.compose.ui.platform.WindowRecomposerFactory getLifecycleAware();
+ property public final androidx.compose.ui.platform.WindowRecomposerFactory LifecycleAware;
+ }
+
+ @androidx.compose.ui.InternalComposeUiApi public final class WindowRecomposerPolicy {
+ method public void setFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory);
+ method public inline <R> R withFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory, kotlin.jvm.functions.Function0<? extends R> block);
+ field public static final androidx.compose.ui.platform.WindowRecomposerPolicy INSTANCE;
+ }
+
+ public final class WindowRecomposer_androidKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.Recomposer createLifecycleAwareWindowRecomposer(android.view.View, optional kotlin.coroutines.CoroutineContext coroutineContext, optional androidx.lifecycle.Lifecycle? lifecycle);
+ method public static androidx.compose.runtime.CompositionContext? findViewTreeCompositionContext(android.view.View);
+ method public static androidx.compose.runtime.CompositionContext? getCompositionContext(android.view.View);
+ method public static void setCompositionContext(android.view.View, androidx.compose.runtime.CompositionContext?);
+ }
+
+}
+
+package androidx.compose.ui.res {
+
+ public final class ColorResources_androidKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long colorResource(@ColorRes int id);
+ }
+
+ public final class FontResources_androidKt {
+ method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static androidx.compose.ui.text.font.Typeface fontResource(androidx.compose.ui.text.font.FontFamily fontFamily);
+ }
+
+ public final class ImageResources_androidKt {
+ method public static androidx.compose.ui.graphics.ImageBitmap imageResource(androidx.compose.ui.graphics.ImageBitmap.Companion, android.content.res.Resources res, @DrawableRes int id);
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.ImageBitmap imageResource(androidx.compose.ui.graphics.ImageBitmap.Companion, @DrawableRes int id);
+ }
+
+ public final class PainterResources_androidKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter painterResource(@DrawableRes int id);
+ }
+
+ public final class PrimitiveResources_androidKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static boolean booleanResource(@BoolRes int id);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static float dimensionResource(@DimenRes int id);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static int[] integerArrayResource(@ArrayRes int id);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static int integerResource(@IntegerRes int id);
+ }
+
+ public final class StringResources_androidKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static String pluralStringResource(@PluralsRes int id, int count);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static String pluralStringResource(@PluralsRes int id, int count, java.lang.Object... formatArgs);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static String![] stringArrayResource(@ArrayRes int id);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static String stringResource(@StringRes int id);
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static String stringResource(@StringRes int id, java.lang.Object... formatArgs);
+ }
+
+ public final class VectorResources_androidKt {
+ method @kotlin.jvm.Throws(exceptionClasses=XmlPullParserException::class) public static androidx.compose.ui.graphics.vector.ImageVector vectorResource(androidx.compose.ui.graphics.vector.ImageVector.Companion, optional android.content.res.Resources.Theme? theme, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.vector.ImageVector vectorResource(androidx.compose.ui.graphics.vector.ImageVector.Companion, @DrawableRes int id);
+ }
+
+}
+
+package androidx.compose.ui.semantics {
+
+ public final class AccessibilityAction<T extends kotlin.Function<? extends java.lang.Boolean>> {
+ ctor public AccessibilityAction(String? label, T? action);
+ method public T? getAction();
+ method public String? getLabel();
+ property public final T? action;
+ property public final String? label;
+ }
+
+ public final class CollectionInfo {
+ ctor public CollectionInfo(int rowCount, int columnCount);
+ method public int getColumnCount();
+ method public int getRowCount();
+ property public final int columnCount;
+ property public final int rowCount;
+ }
+
+ public final class CollectionItemInfo {
+ ctor public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan);
+ method public int getColumnIndex();
+ method public int getColumnSpan();
+ method public int getRowIndex();
+ method public int getRowSpan();
+ property public final int columnIndex;
+ property public final int columnSpan;
+ property public final int rowIndex;
+ property public final int rowSpan;
+ }
+
+ public final class CustomAccessibilityAction {
+ ctor public CustomAccessibilityAction(String label, kotlin.jvm.functions.Function0<java.lang.Boolean> action);
+ method public kotlin.jvm.functions.Function0<java.lang.Boolean> getAction();
+ method public String getLabel();
+ property public final kotlin.jvm.functions.Function0<java.lang.Boolean> action;
+ property public final String label;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class LiveRegionMode {
+ field public static final androidx.compose.ui.semantics.LiveRegionMode.Companion Companion;
+ }
+
+ public static final class LiveRegionMode.Companion {
+ method public int getAssertive();
+ method public int getPolite();
+ property public final int Assertive;
+ property public final int Polite;
+ }
+
+ public final class ProgressBarRangeInfo {
+ ctor public ProgressBarRangeInfo(float current, kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range, optional int steps);
+ method public float getCurrent();
+ method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getRange();
+ method public int getSteps();
+ property public final float current;
+ property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range;
+ property public final int steps;
+ field public static final androidx.compose.ui.semantics.ProgressBarRangeInfo.Companion Companion;
+ }
+
+ public static final class ProgressBarRangeInfo.Companion {
+ method public androidx.compose.ui.semantics.ProgressBarRangeInfo getIndeterminate();
+ property public final androidx.compose.ui.semantics.ProgressBarRangeInfo Indeterminate;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Role {
+ field public static final androidx.compose.ui.semantics.Role.Companion Companion;
+ }
+
+ public static final class Role.Companion {
+ method public int getButton();
+ method public int getCheckbox();
+ method public int getDropdownList();
+ method public int getImage();
+ method public int getRadioButton();
+ method public int getSwitch();
+ method public int getTab();
+ property public final int Button;
+ property public final int Checkbox;
+ property public final int DropdownList;
+ property public final int Image;
+ property public final int RadioButton;
+ property public final int Switch;
+ property public final int Tab;
+ }
+
+ public final class ScrollAxisRange {
+ ctor public ScrollAxisRange(kotlin.jvm.functions.Function0<java.lang.Float> value, kotlin.jvm.functions.Function0<java.lang.Float> maxValue, optional boolean reverseScrolling);
+ method public kotlin.jvm.functions.Function0<java.lang.Float> getMaxValue();
+ method public boolean getReverseScrolling();
+ method public kotlin.jvm.functions.Function0<java.lang.Float> getValue();
+ property public final kotlin.jvm.functions.Function0<java.lang.Float> maxValue;
+ property public final boolean reverseScrolling;
+ property public final kotlin.jvm.functions.Function0<java.lang.Float> value;
+ }
+
+ public final class SemanticsActions {
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getCollapse();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getCopyText();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction>> getCustomActions();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getCutText();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getDismiss();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getExpand();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> getGetTextLayoutResult();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> getInsertTextAtCursor();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnClick();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnLongClick();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageDown();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageLeft();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageRight();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageUp();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPasteText();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPerformImeAction();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getRequestFocus();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Float,java.lang.Boolean>>> getScrollBy();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Boolean>>> getScrollToIndex();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Boolean>>> getSetProgress();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function3<java.lang.Integer,java.lang.Integer,java.lang.Boolean,java.lang.Boolean>>> getSetSelection();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> getSetText();
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Collapse;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> CopyText;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction>> CustomActions;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> CutText;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Dismiss;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Expand;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> GetTextLayoutResult;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> InsertTextAtCursor;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnClick;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnLongClick;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageDown;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageLeft;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageRight;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageUp;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PasteText;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PerformImeAction;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> RequestFocus;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Float,java.lang.Boolean>>> ScrollBy;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.lang.Integer,java.lang.Boolean>>> ScrollToIndex;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Boolean>>> SetProgress;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function3<java.lang.Integer,java.lang.Integer,java.lang.Boolean,java.lang.Boolean>>> SetSelection;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> SetText;
+ field public static final androidx.compose.ui.semantics.SemanticsActions INSTANCE;
+ }
+
+ public final class SemanticsConfiguration implements java.lang.Iterable<java.util.Map.Entry<? extends androidx.compose.ui.semantics.SemanticsPropertyKey<?>,?>> kotlin.jvm.internal.markers.KMappedMarker androidx.compose.ui.semantics.SemanticsPropertyReceiver {
+ ctor public SemanticsConfiguration();
+ method public operator <T> boolean contains(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key);
+ method public androidx.compose.ui.semantics.SemanticsConfiguration copy();
+ method public operator <T> T get(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key);
+ method public <T> T getOrElse(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public <T> T? getOrElseNullable(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public boolean isClearingSemantics();
+ method public boolean isMergingSemanticsOfDescendants();
+ method public java.util.Iterator<java.util.Map.Entry<androidx.compose.ui.semantics.SemanticsPropertyKey<?>,java.lang.Object>> iterator();
+ method public <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T value);
+ method public void setClearingSemantics(boolean);
+ method public void setMergingSemanticsOfDescendants(boolean);
+ property public final boolean isClearingSemantics;
+ property public final boolean isMergingSemanticsOfDescendants;
+ }
+
+ public final class SemanticsConfigurationKt {
+ method public static <T> T? getOrNull(androidx.compose.ui.semantics.SemanticsConfiguration, androidx.compose.ui.semantics.SemanticsPropertyKey<T> key);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public interface SemanticsModifier extends androidx.compose.ui.Modifier.Element {
+ method @Deprecated public default int getId();
+ method public androidx.compose.ui.semantics.SemanticsConfiguration getSemanticsConfiguration();
+ property @Deprecated public default int id;
+ property public abstract androidx.compose.ui.semantics.SemanticsConfiguration semanticsConfiguration;
+ }
+
+ public final class SemanticsModifierKt {
+ method public static androidx.compose.ui.Modifier clearAndSetSemantics(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+ method public static androidx.compose.ui.Modifier semantics(androidx.compose.ui.Modifier, optional boolean mergeDescendants, kotlin.jvm.functions.Function1<? super androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+ }
+
+ public final class SemanticsNode {
+ method public int getAlignmentLinePosition(androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public androidx.compose.ui.geometry.Rect getBoundsInRoot();
+ method public androidx.compose.ui.geometry.Rect getBoundsInWindow();
+ method public java.util.List<androidx.compose.ui.semantics.SemanticsNode> getChildren();
+ method public androidx.compose.ui.semantics.SemanticsConfiguration getConfig();
+ method public int getId();
+ method public androidx.compose.ui.layout.LayoutInfo getLayoutInfo();
+ method public boolean getMergingEnabled();
+ method public androidx.compose.ui.semantics.SemanticsNode? getParent();
+ method public long getPositionInRoot();
+ method public long getPositionInWindow();
+ method public androidx.compose.ui.node.RootForTest? getRoot();
+ method public long getSize();
+ method public androidx.compose.ui.geometry.Rect getTouchBoundsInRoot();
+ method public boolean isRoot();
+ property public final androidx.compose.ui.geometry.Rect boundsInRoot;
+ property public final androidx.compose.ui.geometry.Rect boundsInWindow;
+ property public final java.util.List<androidx.compose.ui.semantics.SemanticsNode> children;
+ property public final androidx.compose.ui.semantics.SemanticsConfiguration config;
+ property public final int id;
+ property public final boolean isRoot;
+ property public final androidx.compose.ui.layout.LayoutInfo layoutInfo;
+ property public final boolean mergingEnabled;
+ property public final androidx.compose.ui.semantics.SemanticsNode? parent;
+ property public final long positionInRoot;
+ property public final long positionInWindow;
+ property public final androidx.compose.ui.node.RootForTest? root;
+ property public final long size;
+ property public final androidx.compose.ui.geometry.Rect touchBoundsInRoot;
+ }
+
+ public final class SemanticsOwner {
+ method public androidx.compose.ui.semantics.SemanticsNode getRootSemanticsNode();
+ method public androidx.compose.ui.semantics.SemanticsNode getUnmergedRootSemanticsNode();
+ property public final androidx.compose.ui.semantics.SemanticsNode rootSemanticsNode;
+ property public final androidx.compose.ui.semantics.SemanticsNode unmergedRootSemanticsNode;
+ }
+
+ public final class SemanticsOwnerKt {
+ method public static java.util.List<androidx.compose.ui.semantics.SemanticsNode> getAllSemanticsNodes(androidx.compose.ui.semantics.SemanticsOwner, boolean mergingEnabled);
+ }
+
+ public final class SemanticsProperties {
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> getCollectionInfo();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> getCollectionItemInfo();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getDisabled();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> getEditableText();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getError();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getFocused();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getHeading();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getHorizontalScrollAxisRange();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.input.ImeAction> getImeAction();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer>> getIndexForKey();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getInvisibleToUser();
+ method @Deprecated public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getIsContainer();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsDialog();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getIsPopup();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getIsTraversalGroup();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.LiveRegionMode> getLiveRegion();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getPaneTitle();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getPassword();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> getProgressBarRangeInfo();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> getRole();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> getSelectableGroup();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getSelected();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getStateDescription();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<androidx.compose.ui.text.AnnotatedString>> getText();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> getTextSelectionRange();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> getToggleableState();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Float> getTraversalIndex();
+ method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> getVerticalScrollAxisRange();
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionInfo> CollectionInfo;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.CollectionItemInfo> CollectionItemInfo;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Disabled;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.AnnotatedString> EditableText;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> Error;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Focused;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Heading;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> HorizontalScrollAxisRange;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.input.ImeAction> ImeAction;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer>> IndexForKey;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> InvisibleToUser;
+ property @Deprecated public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> IsContainer;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsDialog;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> IsPopup;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> IsTraversalGroup;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.LiveRegionMode> LiveRegion;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> PaneTitle;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> Password;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ProgressBarRangeInfo> ProgressBarRangeInfo;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.Role> Role;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<kotlin.Unit> SelectableGroup;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> Selected;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> StateDescription;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.util.List<androidx.compose.ui.text.AnnotatedString>> Text;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.text.TextRange> TextSelectionRange;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.state.ToggleableState> ToggleableState;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Float> TraversalIndex;
+ property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.ScrollAxisRange> VerticalScrollAxisRange;
+ field public static final androidx.compose.ui.semantics.SemanticsProperties INSTANCE;
+ }
+
+ @androidx.compose.ui.ExperimentalComposeUiApi public final class SemanticsPropertiesAndroid {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getTestTagsAsResourceId();
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> TestTagsAsResourceId;
+ field public static final androidx.compose.ui.semantics.SemanticsPropertiesAndroid INSTANCE;
+ }
+
+ public final class SemanticsPropertiesKt {
+ method public static void collapse(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void copyText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void cutText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void dialog(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void disabled(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void dismiss(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void error(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String description);
+ method public static void expand(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static androidx.compose.ui.semantics.CollectionInfo getCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.semantics.CollectionItemInfo getCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static String getContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction> getCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.text.AnnotatedString getEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static boolean getFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.semantics.ScrollAxisRange getHorizontalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method @Deprecated public static int getImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static int getLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static String getPaneTitle(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.semantics.ProgressBarRangeInfo getProgressBarRangeInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static int getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.text.AnnotatedString getText(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void getTextLayoutResult(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>? action);
+ method public static long getTextSelectionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.state.ToggleableState getToggleableState(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static float getTraversalIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static androidx.compose.ui.semantics.ScrollAxisRange getVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
+ method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method @Deprecated public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static boolean isTraversalGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void pageDown(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void pageLeft(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void pageRight(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void pageUp(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void password(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void pasteText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void performImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver, int imeActionType, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void popup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void requestFocus(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
+ method public static void scrollBy(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Boolean>? action);
+ method public static void scrollToIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> action);
+ method public static void selectableGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method public static void setCollectionInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionInfo);
+ method public static void setCollectionItemInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.CollectionItemInfo);
+ method @Deprecated public static void setContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
+ method public static void setContentDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String);
+ method public static void setCustomActions(androidx.compose.ui.semantics.SemanticsPropertyReceiver, java.util.List<androidx.compose.ui.semantics.CustomAccessibilityAction>);
+ method public static void setEditableText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString);
+ method public static void setFocused(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
+ method public static void setHorizontalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.ScrollAxisRange);
+ method @Deprecated public static void setImeAction(androidx.compose.ui.semantics.SemanticsPropertyReceiver, int);
+ method public static void setLiveRegion(androidx.compose.ui.semantics.SemanticsPropertyReceiver, int);
+ method public static void setPaneTitle(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String);
+ method public static void setProgress(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean>? action);
+ method public static void setProgressBarRangeInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.ProgressBarRangeInfo);
+ method public static void setRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver, int);
+ method public static void setSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
+ method public static void setSelection(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function3<? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Boolean,java.lang.Boolean>? action);
+ method public static void setStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String);
+ method public static void setTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver, String);
+ method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.text.AnnotatedString);
+ method public static void setText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
+ method public static void setTextSelectionRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, long);
+ method public static void setToggleableState(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.state.ToggleableState);
+ method public static void setTraversalGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
+ method public static void setTraversalIndex(androidx.compose.ui.semantics.SemanticsPropertyReceiver, float);
+ method public static void setVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.ScrollAxisRange);
+ }
+
+ public final class SemanticsProperties_androidKt {
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public static void setTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
+ }
+
+ public final class SemanticsPropertyKey<T> {
+ ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+ method public String getName();
+ method public operator T getValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver thisRef, kotlin.reflect.KProperty<?> property);
+ method public T? merge(T? parentValue, T childValue);
+ method public operator void setValue(androidx.compose.ui.semantics.SemanticsPropertyReceiver thisRef, kotlin.reflect.KProperty<?> property, T value);
+ property public final String name;
+ }
+
+ public interface SemanticsPropertyReceiver {
+ method public operator <T> void set(androidx.compose.ui.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+}
+
+package androidx.compose.ui.state {
+
+ public enum ToggleableState {
+ method public static androidx.compose.ui.state.ToggleableState valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.state.ToggleableState[] values();
+ enum_constant public static final androidx.compose.ui.state.ToggleableState Indeterminate;
+ enum_constant public static final androidx.compose.ui.state.ToggleableState Off;
+ enum_constant public static final androidx.compose.ui.state.ToggleableState On;
+ }
+
+ public final class ToggleableStateKt {
+ method public static androidx.compose.ui.state.ToggleableState ToggleableState(boolean value);
+ }
+
+}
+
+package androidx.compose.ui.text {
+
+ public final class TextMeasurerHelperKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.ui.text.TextMeasurer rememberTextMeasurer(optional int cacheSize);
+ }
+
+}
+
+package androidx.compose.ui.viewinterop {
+
+ public final class AndroidView_androidKt {
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onReset, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onRelease, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
+ method public static kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> getNoOpUpdate();
+ property public static final kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> NoOpUpdate;
+ }
+
+}
+
+package androidx.compose.ui.window {
+
+ public final class AndroidDialog_androidKt {
+ method @androidx.compose.runtime.Composable public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.window.DialogProperties properties, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class AndroidPopup_androidKt {
+ method @androidx.compose.runtime.Composable public static void Popup(optional androidx.compose.ui.Alignment alignment, optional long offset, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDismissRequest, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void Popup(androidx.compose.ui.window.PopupPositionProvider popupPositionProvider, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDismissRequest, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @org.jetbrains.annotations.TestOnly public static boolean isPopupLayout(android.view.View view, optional String? testTag);
+ }
+
+ @androidx.compose.runtime.Immutable public final class DialogProperties {
+ ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
+ ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean usePlatformDefaultWidth, optional boolean decorFitsSystemWindows);
+ method public boolean getDecorFitsSystemWindows();
+ method public boolean getDismissOnBackPress();
+ method public boolean getDismissOnClickOutside();
+ method public androidx.compose.ui.window.SecureFlagPolicy getSecurePolicy();
+ method public boolean getUsePlatformDefaultWidth();
+ property public final boolean decorFitsSystemWindows;
+ property public final boolean dismissOnBackPress;
+ property public final boolean dismissOnClickOutside;
+ property public final androidx.compose.ui.window.SecureFlagPolicy securePolicy;
+ property public final boolean usePlatformDefaultWidth;
+ }
+
+ public interface DialogWindowProvider {
+ method public android.view.Window getWindow();
+ property public abstract android.view.Window window;
+ }
+
+ @androidx.compose.runtime.Immutable public interface PopupPositionProvider {
+ method public long calculatePosition(androidx.compose.ui.unit.IntRect anchorBounds, long windowSize, androidx.compose.ui.unit.LayoutDirection layoutDirection, long popupContentSize);
+ }
+
+ @androidx.compose.runtime.Immutable public final class PopupProperties {
+ ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled);
+ ctor @androidx.compose.ui.ExperimentalComposeUiApi public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled, optional boolean usePlatformDefaultWidth);
+ method public boolean getClippingEnabled();
+ method public boolean getDismissOnBackPress();
+ method public boolean getDismissOnClickOutside();
+ method public boolean getExcludeFromSystemGesture();
+ method public boolean getFocusable();
+ method public androidx.compose.ui.window.SecureFlagPolicy getSecurePolicy();
+ method public boolean getUsePlatformDefaultWidth();
+ property public final boolean clippingEnabled;
+ property public final boolean dismissOnBackPress;
+ property public final boolean dismissOnClickOutside;
+ property public final boolean excludeFromSystemGesture;
+ property public final boolean focusable;
+ property public final androidx.compose.ui.window.SecureFlagPolicy securePolicy;
+ property public final boolean usePlatformDefaultWidth;
+ }
+
+ public enum SecureFlagPolicy {
+ method public static androidx.compose.ui.window.SecureFlagPolicy valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.ui.window.SecureFlagPolicy[] values();
+ enum_constant public static final androidx.compose.ui.window.SecureFlagPolicy Inherit;
+ enum_constant public static final androidx.compose.ui.window.SecureFlagPolicy SecureOff;
+ enum_constant public static final androidx.compose.ui.window.SecureFlagPolicy SecureOn;
+ }
+
+}
+
diff --git a/compose/ui/ui/api/restricted_1.5.0-beta01.txt b/compose/ui/ui/api/restricted_1.5.0-beta01.txt
index d24ac94..c75c01f 100644
--- a/compose/ui/ui/api/restricted_1.5.0-beta01.txt
+++ b/compose/ui/ui/api/restricted_1.5.0-beta01.txt
@@ -112,21 +112,11 @@
}
public final class ComposedModifierKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, Object? key3, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object![]? keys, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method @Deprecated public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
}
- @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalComposeUiApi {
- }
-
- @kotlin.RequiresOptIn(message="Unstable API for use only between compose-ui modules sharing the same exact version, " + "subject to change without notice in major, minor, or patch releases.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface InternalComposeUiApi {
- }
-
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
method public boolean all(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
method public boolean any(kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier.Element,java.lang.Boolean> predicate);
@@ -159,7 +149,6 @@
method public void onAttach();
method public void onDetach();
method public void onReset();
- method @androidx.compose.ui.ExperimentalComposeUiApi public final void sideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
property public final boolean isAttached;
property public final androidx.compose.ui.Modifier.Node node;
@@ -186,77 +175,6 @@
}
-package androidx.compose.ui.autofill {
-
- @androidx.compose.ui.ExperimentalComposeUiApi public interface Autofill {
- method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillNode {
- ctor public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
- method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
- method public androidx.compose.ui.geometry.Rect? getBoundingBox();
- method public int getId();
- method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
- method public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
- property public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
- property public final androidx.compose.ui.geometry.Rect? boundingBox;
- property public final int id;
- property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public final class AutofillTree {
- ctor public AutofillTree();
- method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
- method public kotlin.Unit? performAutofill(int id, String value);
- method public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
- property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public enum AutofillType {
- method public static androidx.compose.ui.autofill.AutofillType valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
- method public static androidx.compose.ui.autofill.AutofillType[] values();
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Gender;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewPassword;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewUsername;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Password;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Username;
- }
-
-}
-
package androidx.compose.ui.draw {
public final class AlphaKt {
@@ -359,22 +277,14 @@
public static final class FocusDirection.Companion {
method public int getDown();
- method @androidx.compose.ui.ExperimentalComposeUiApi public int getEnter();
- method @androidx.compose.ui.ExperimentalComposeUiApi public int getExit();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getIn();
method public int getLeft();
method public int getNext();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getOut();
method public int getPrevious();
method public int getRight();
method public int getUp();
property public final int Down;
- property @androidx.compose.ui.ExperimentalComposeUiApi public final int Enter;
- property @androidx.compose.ui.ExperimentalComposeUiApi public final int Exit;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int In;
property public final int Left;
property public final int Next;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Out;
property public final int Previous;
property public final int Right;
property public final int Up;
@@ -444,8 +354,6 @@
method public boolean getCanFocus();
method public default androidx.compose.ui.focus.FocusRequester getDown();
method public default androidx.compose.ui.focus.FocusRequester getEnd();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getEnter();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> getExit();
method public default androidx.compose.ui.focus.FocusRequester getLeft();
method public default androidx.compose.ui.focus.FocusRequester getNext();
method public default androidx.compose.ui.focus.FocusRequester getPrevious();
@@ -455,8 +363,6 @@
method public void setCanFocus(boolean);
method public default void setDown(androidx.compose.ui.focus.FocusRequester);
method public default void setEnd(androidx.compose.ui.focus.FocusRequester);
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void setEnter(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void setExit(kotlin.jvm.functions.Function1<? super androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester>);
method public default void setLeft(androidx.compose.ui.focus.FocusRequester);
method public default void setNext(androidx.compose.ui.focus.FocusRequester);
method public default void setPrevious(androidx.compose.ui.focus.FocusRequester);
@@ -466,8 +372,6 @@
property public abstract boolean canFocus;
property public default androidx.compose.ui.focus.FocusRequester down;
property public default androidx.compose.ui.focus.FocusRequester end;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> enter;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default kotlin.jvm.functions.Function1<androidx.compose.ui.focus.FocusDirection,androidx.compose.ui.focus.FocusRequester> exit;
property public default androidx.compose.ui.focus.FocusRequester left;
property public default androidx.compose.ui.focus.FocusRequester next;
property public default androidx.compose.ui.focus.FocusRequester previous;
@@ -497,33 +401,10 @@
}
public static final class FocusRequester.Companion {
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory createRefs();
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.focus.FocusRequester getCancel();
method public androidx.compose.ui.focus.FocusRequester getDefault();
- property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.focus.FocusRequester Cancel;
property public final androidx.compose.ui.focus.FocusRequester Default;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public static final class FocusRequester.Companion.FocusRequesterFactory {
- method public operator androidx.compose.ui.focus.FocusRequester component1();
- method public operator androidx.compose.ui.focus.FocusRequester component10();
- method public operator androidx.compose.ui.focus.FocusRequester component11();
- method public operator androidx.compose.ui.focus.FocusRequester component12();
- method public operator androidx.compose.ui.focus.FocusRequester component13();
- method public operator androidx.compose.ui.focus.FocusRequester component14();
- method public operator androidx.compose.ui.focus.FocusRequester component15();
- method public operator androidx.compose.ui.focus.FocusRequester component16();
- method public operator androidx.compose.ui.focus.FocusRequester component2();
- method public operator androidx.compose.ui.focus.FocusRequester component3();
- method public operator androidx.compose.ui.focus.FocusRequester component4();
- method public operator androidx.compose.ui.focus.FocusRequester component5();
- method public operator androidx.compose.ui.focus.FocusRequester component6();
- method public operator androidx.compose.ui.focus.FocusRequester component7();
- method public operator androidx.compose.ui.focus.FocusRequester component8();
- method public operator androidx.compose.ui.focus.FocusRequester component9();
- field public static final androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory INSTANCE;
- }
-
@Deprecated @kotlin.jvm.JvmDefaultWithCompatibility public interface FocusRequesterModifier extends androidx.compose.ui.Modifier.Element {
method @Deprecated public androidx.compose.ui.focus.FocusRequester getFocusRequester();
property @Deprecated public abstract androidx.compose.ui.focus.FocusRequester focusRequester;
@@ -945,7 +826,6 @@
public interface InputModeManager {
method public int getInputMode();
- method @androidx.compose.ui.ExperimentalComposeUiApi public boolean requestInputMode(int inputMode);
property public abstract int inputMode;
}
@@ -1581,16 +1461,6 @@
method public static int getNativeKeyCode(long);
}
- @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftKeyboardInterceptionModifierNode extends androidx.compose.ui.node.DelegatableNode {
- method public boolean onInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
- method public boolean onPreInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
- }
-
- public final class SoftwareKeyboardInterceptionModifierKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onInterceptKeyBeforeSoftKeyboard);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onPreInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreInterceptKeyBeforeSoftKeyboard);
- }
-
}
package androidx.compose.ui.input.nestedscroll {
@@ -1627,10 +1497,8 @@
public static final class NestedScrollSource.Companion {
method public int getDrag();
method public int getFling();
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public int getRelocate();
property public final int Drag;
property public final int Fling;
- property @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final int Relocate;
}
}
@@ -1661,14 +1529,6 @@
property @Deprecated public final boolean positionChange;
}
- @androidx.compose.runtime.Immutable @androidx.compose.ui.ExperimentalComposeUiApi public final class HistoricalChange {
- ctor public HistoricalChange(long uptimeMillis, long position);
- method public long getPosition();
- method public long getUptimeMillis();
- property public final long position;
- property public final long uptimeMillis;
- }
-
@kotlin.jvm.JvmInline public final value class PointerButtons {
ctor public PointerButtons(int packedValue);
}
@@ -1798,14 +1658,11 @@
ctor @Deprecated 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 public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, boolean isInitiallyConsumed, optional int type, optional long scrollDelta);
method public void consume();
- 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 float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
method public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional float pressure, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional int type, optional long scrollDelta);
method @Deprecated 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 @Deprecated 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, androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type, optional long scrollDelta);
- 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 int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical, optional long scrollDelta);
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 int type, optional long scrollDelta);
method @Deprecated public androidx.compose.ui.input.pointer.ConsumedData getConsumed();
- method @androidx.compose.ui.ExperimentalComposeUiApi public java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> getHistorical();
method public long getId();
method public long getPosition();
method public boolean getPressed();
@@ -1818,7 +1675,6 @@
method public long getUptimeMillis();
method public boolean isConsumed();
property @Deprecated 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 public final boolean isConsumed;
property public final long position;
@@ -1862,11 +1718,6 @@
property public abstract androidx.compose.ui.platform.ViewConfiguration viewConfiguration;
}
- public final class PointerInteropFilter_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier motionEventSpy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,kotlin.Unit> watcher);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier pointerInteropFilter(androidx.compose.ui.Modifier, optional androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent? requestDisallowInterceptTouchEvent, kotlin.jvm.functions.Function1<? super android.view.MotionEvent,java.lang.Boolean> onTouchEvent);
- }
-
@kotlin.jvm.JvmInline public final value class PointerKeyboardModifiers {
ctor public PointerKeyboardModifiers(int packedValue);
}
@@ -1888,11 +1739,6 @@
property public final int Unknown;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class RequestDisallowInterceptTouchEvent implements kotlin.jvm.functions.Function1<java.lang.Boolean,kotlin.Unit> {
- ctor public RequestDisallowInterceptTouchEvent();
- method public void invoke(boolean disallowIntercept);
- }
-
public final class SuspendingPointerInputFilterKt {
method public static androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode SuspendingPointerInputModifierNode(kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> pointerInputHandler);
method public static androidx.compose.ui.Modifier pointerInput(androidx.compose.ui.Modifier, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
@@ -1930,9 +1776,6 @@
public final class VelocityTrackerKt {
method public static void addPointerInputChange(androidx.compose.ui.input.pointer.util.VelocityTracker, androidx.compose.ui.input.pointer.PointerInputChange event);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getVelocityTrackerAddPointsFix();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void setVelocityTrackerAddPointsFix(boolean);
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final boolean VelocityTrackerAddPointsFix;
}
}
@@ -2052,11 +1895,6 @@
ctor public HorizontalAlignmentLine(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,java.lang.Integer> merger);
}
- @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface IntermediateMeasureScope extends androidx.compose.ui.layout.LookaheadScope kotlinx.coroutines.CoroutineScope androidx.compose.ui.layout.MeasureScope {
- method public long getLookaheadSize();
- property public abstract long lookaheadSize;
- }
-
public interface IntrinsicMeasurable {
method public Object? getParentData();
method public int maxIntrinsicHeight(int width);
@@ -2068,8 +1906,6 @@
public interface IntrinsicMeasureScope extends androidx.compose.ui.unit.Density {
method public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead();
- property @androidx.compose.ui.ExperimentalComposeUiApi public default boolean isLookingAhead;
property public abstract androidx.compose.ui.unit.LayoutDirection layoutDirection;
}
@@ -2159,27 +1995,6 @@
method public static androidx.compose.ui.Modifier layout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
}
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface LookaheadLayoutCoordinates extends androidx.compose.ui.layout.LayoutCoordinates {
- }
-
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadLayoutScope {
- method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
- }
-
- @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadScope {
- method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method @Deprecated public default androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function4<? super androidx.compose.ui.layout.MeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? super androidx.compose.ui.unit.IntSize,? extends androidx.compose.ui.layout.MeasureResult> measure);
- method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
- method @Deprecated public androidx.compose.ui.Modifier onPlaced(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,? super androidx.compose.ui.layout.LookaheadLayoutCoordinates,kotlin.Unit> onPlaced);
- method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
- }
-
- public final class LookaheadScopeKt {
- method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadLayout(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
- method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntermediateMeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
- }
-
public interface Measurable extends androidx.compose.ui.layout.IntrinsicMeasurable {
method public androidx.compose.ui.layout.Placeable measure(long constraints);
}
@@ -2318,24 +2133,6 @@
property protected abstract int parentWidth;
}
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi @kotlin.jvm.JvmDefaultWithCompatibility public interface RelocationModifier extends androidx.compose.ui.Modifier.Element {
- method @Deprecated public androidx.compose.ui.geometry.Rect computeDestination(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);
- method @Deprecated public suspend Object? performRelocation(androidx.compose.ui.geometry.Rect source, androidx.compose.ui.geometry.Rect destination, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class RelocationModifierKt {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onRelocationRequest(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.layout.LayoutCoordinates,androidx.compose.ui.geometry.Rect> onProvideDestination, kotlin.jvm.functions.Function3<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.geometry.Rect,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> onPerformRelocation);
- }
-
- @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public final class RelocationRequester {
- ctor @Deprecated public RelocationRequester();
- method @Deprecated public suspend Object? bringIntoView(optional androidx.compose.ui.geometry.Rect? rect, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- }
-
- public final class RelocationRequesterModifierKt {
- method @Deprecated @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier relocationRequester(androidx.compose.ui.Modifier, Object relocationRequester);
- }
-
public interface Remeasurement {
method public void forceRemeasure();
}
@@ -2427,10 +2224,6 @@
method public void onModifierLocalsUpdated(androidx.compose.ui.modifier.ModifierLocalReadScope scope);
}
- public final class ModifierLocalConsumerKt {
- method @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier modifierLocalConsumer(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.modifier.ModifierLocalReadScope,kotlin.Unit> consumer);
- }
-
public final class ModifierLocalKt {
method public static <T> androidx.compose.ui.modifier.ProvidableModifierLocal<T> modifierLocalOf(kotlin.jvm.functions.Function0<? extends T> defaultFactory);
}
@@ -2460,10 +2253,6 @@
property public abstract T value;
}
- public final class ModifierLocalProviderKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static <T> androidx.compose.ui.Modifier modifierLocalProvider(androidx.compose.ui.Modifier, androidx.compose.ui.modifier.ProvidableModifierLocal<T> key, kotlin.jvm.functions.Function0<? extends T> value);
- }
-
public interface ModifierLocalReadScope {
method public <T> T getCurrent(androidx.compose.ui.modifier.ModifierLocal<T>);
}
@@ -2558,13 +2347,6 @@
method public void onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates coordinates);
}
- @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY_SETTER}) public @interface InternalCoreApi {
- }
-
- @androidx.compose.ui.InternalComposeUiApi public sealed interface InteroperableComposeUiNode {
- method public android.view.View? getInteropView();
- }
-
public interface LayoutAwareModifierNode extends androidx.compose.ui.node.DelegatableNode {
method public default void onPlaced(androidx.compose.ui.layout.LayoutCoordinates coordinates);
method public default void onRemeasured(long size);
@@ -2636,7 +2418,6 @@
method public androidx.compose.ui.unit.Density getDensity();
method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
method public androidx.compose.ui.text.input.TextInputService getTextInputService();
- method @androidx.compose.ui.ExperimentalComposeUiApi public default void measureAndLayoutForTest();
method public boolean sendKeyEvent(android.view.KeyEvent keyEvent);
property public abstract androidx.compose.ui.unit.Density density;
property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
@@ -2753,8 +2534,6 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> getLocalAutofill();
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> getLocalDensity();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> getLocalFocusManager();
@@ -2762,15 +2541,12 @@
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> getLocalHapticFeedback();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> getLocalInputModeManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> getLocalLayoutDirection();
- method @androidx.compose.ui.text.ExperimentalTextApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> getLocalPlatformTextInputPluginRegistry();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> getLocalTextInputService();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> getLocalTextToolbar();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> getLocalUriHandler();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> getLocalViewConfiguration();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> getLocalWindowInfo();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> LocalAccessibilityManager;
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> LocalAutofill;
- property @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> LocalClipboardManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Density> LocalDensity;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.focus.FocusManager> LocalFocusManager;
@@ -2778,7 +2554,6 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.hapticfeedback.HapticFeedback> LocalHapticFeedback;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.input.InputModeManager> LocalInputModeManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.LayoutDirection> LocalLayoutDirection;
- property @androidx.compose.ui.text.ExperimentalTextApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.PlatformTextInputPluginRegistry> LocalPlatformTextInputPluginRegistry;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.input.TextInputService> LocalTextInputService;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.TextToolbar> LocalTextToolbar;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.UriHandler> LocalUriHandler;
@@ -2850,24 +2625,10 @@
property public Object? valueOverride;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class LocalSoftwareKeyboardController {
- method @androidx.compose.runtime.Composable public androidx.compose.ui.platform.SoftwareKeyboardController? getCurrent();
- method public infix androidx.compose.runtime.ProvidedValue<androidx.compose.ui.platform.SoftwareKeyboardController> provides(androidx.compose.ui.platform.SoftwareKeyboardController softwareKeyboardController);
- property @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.platform.SoftwareKeyboardController? current;
- field public static final androidx.compose.ui.platform.LocalSoftwareKeyboardController INSTANCE;
- }
-
public final class NestedScrollInteropConnectionKt {
method @androidx.compose.runtime.Composable public static androidx.compose.ui.input.nestedscroll.NestedScrollConnection rememberNestedScrollInteropConnection(optional android.view.View hostView);
}
- @androidx.compose.runtime.Stable @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftwareKeyboardController {
- method public void hide();
- method @Deprecated public default void hideSoftwareKeyboard();
- method public void show();
- method @Deprecated public default void showSoftwareKeyboard();
- }
-
public final class TestTagKt {
method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier testTag(androidx.compose.ui.Modifier, String tag);
}
@@ -2976,32 +2737,11 @@
}
@androidx.compose.runtime.Stable public interface WindowInfo {
- method @androidx.compose.ui.ExperimentalComposeUiApi public default int getKeyboardModifiers();
method public boolean isWindowFocused();
property public abstract boolean isWindowFocused;
- property @androidx.compose.ui.ExperimentalComposeUiApi public default int keyboardModifiers;
- }
-
- @androidx.compose.ui.InternalComposeUiApi public fun interface WindowRecomposerFactory {
- method public androidx.compose.runtime.Recomposer createRecomposer(android.view.View windowRootView);
- field public static final androidx.compose.ui.platform.WindowRecomposerFactory.Companion Companion;
- }
-
- public static final class WindowRecomposerFactory.Companion {
- method public androidx.compose.ui.platform.WindowRecomposerFactory getLifecycleAware();
- property public final androidx.compose.ui.platform.WindowRecomposerFactory LifecycleAware;
- }
-
- @androidx.compose.ui.InternalComposeUiApi public final class WindowRecomposerPolicy {
- method @kotlin.PublishedApi internal boolean compareAndSetFactory(androidx.compose.ui.platform.WindowRecomposerFactory expected, androidx.compose.ui.platform.WindowRecomposerFactory factory);
- method @kotlin.PublishedApi internal androidx.compose.ui.platform.WindowRecomposerFactory getAndSetFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory);
- method public void setFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory);
- method public inline <R> R withFactory(androidx.compose.ui.platform.WindowRecomposerFactory factory, kotlin.jvm.functions.Function0<? extends R> block);
- field public static final androidx.compose.ui.platform.WindowRecomposerPolicy INSTANCE;
}
public final class WindowRecomposer_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.Recomposer createLifecycleAwareWindowRecomposer(android.view.View, optional kotlin.coroutines.CoroutineContext coroutineContext, optional androidx.lifecycle.Lifecycle? lifecycle);
method public static androidx.compose.runtime.CompositionContext? findViewTreeCompositionContext(android.view.View);
method public static androidx.compose.runtime.CompositionContext? getCompositionContext(android.view.View);
method public static void setCompositionContext(android.view.View, androidx.compose.runtime.CompositionContext?);
@@ -3334,12 +3074,6 @@
field public static final androidx.compose.ui.semantics.SemanticsProperties INSTANCE;
}
- @androidx.compose.ui.ExperimentalComposeUiApi public final class SemanticsPropertiesAndroid {
- method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> getTestTagsAsResourceId();
- property @androidx.compose.ui.ExperimentalComposeUiApi public final androidx.compose.ui.semantics.SemanticsPropertyKey<java.lang.Boolean> TestTagsAsResourceId;
- field public static final androidx.compose.ui.semantics.SemanticsPropertiesAndroid INSTANCE;
- }
-
public final class SemanticsPropertiesKt {
method public static void collapse(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
method public static void copyText(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3373,7 +3107,6 @@
method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method @Deprecated public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static boolean isTraversalGroup(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3417,11 +3150,6 @@
method public static void setVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver, androidx.compose.ui.semantics.ScrollAxisRange);
}
- public final class SemanticsProperties_androidKt {
- method @androidx.compose.ui.ExperimentalComposeUiApi public static boolean getTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
- method @androidx.compose.ui.ExperimentalComposeUiApi public static void setTestTagsAsResourceId(androidx.compose.ui.semantics.SemanticsPropertyReceiver, boolean);
- }
-
public final class SemanticsPropertyKey<T> {
ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
method public String getName();
@@ -3510,7 +3238,6 @@
@androidx.compose.runtime.Immutable public final class PopupProperties {
ctor public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled);
- ctor @androidx.compose.ui.ExperimentalComposeUiApi public PopupProperties(optional boolean focusable, optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean excludeFromSystemGesture, optional boolean clippingEnabled, optional boolean usePlatformDefaultWidth);
method public boolean getClippingEnabled();
method public boolean getDismissOnBackPress();
method public boolean getDismissOnClickOutside();
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
index 4412d11..7078017 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
@@ -37,3 +37,4 @@
android {
namespace "androidx.constraintlayout.compose.demos"
}
+
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml b/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml
new file mode 100644
index 0000000..9a0fa82
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.1.0-alpha07">
+
+ <issue
+ id="LintError"
+ message="Unexpected failure during lint analysis of ChainsDemo.kt (this is a bug in lint or one of the libraries it depends on)

Message: org/jetbrains/uast/visitor/UastVisitor$DefaultImpls

The crash seems to involve the detector `androidx.constraintlayout.compose.lint.ConstraintLayoutDslDetector`.
You can try disabling it with something like this:
 android {
 lint {
 disable "IncorrectReferencesDeclaration", "IncorrectMatchParentUsage", "IncorrectChainMarginsUsage"
 }
 }

Stack: `NoClassDefFoundError:ConstraintLayoutDslDetectorKt$findChildIdentifier$1.afterVisitSimpleNameReferenceExpression(ConstraintLayoutDslDetector.kt:733)←KotlinUSimpleReferenceExpression.accept(KotlinUSimpleReferenceExpression.kt:47)←ConstraintLayoutDslDetectorKt.findChildIdentifier(ConstraintLayoutDslDetector.kt:732)←ConstraintLayoutDslDetector$createUastHandler$1.detectChainParamsUsage(ConstraintLayoutDslDetector.kt:338)←ConstraintLayoutDslDetector$createUastHandler$1.visitCallExpression(ConstraintLayoutDslDetector.kt:144)←UElementVisitor$DispatchPsiVisitor.visitCallExpression(UElementVisitor.kt:511)←UElementVisitor$DelegatingPsiVisitor.visitCallExpression(UElementVisitor.kt:1057)←KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:165)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←UBlockExpression.accept(UBlockExpression.kt:21)←UIfExpression.accept(UIfExpression.kt:59)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←UBlockExpression.accept(UBlockExpression.kt:21)←ULambdaExpression.accept(ULambdaExpression.kt:40)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:169)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:169)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←UBlockExpression.accept(UBlockExpression.kt:21)←ULambdaExpression.accept(ULambdaExpression.kt:40)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:169)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←UBlockExpression.accept(UBlockExpression.kt:21)←UMethod.accept(UMethod.kt:45)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←AbstractKotlinUClass.accept(AbstractKotlinUClass.kt:99)←ImplementationUtilsKt.acceptList(implementationUtils.kt:14)←UFile.accept(UFile.kt:89)←UastLintUtilsKt.acceptSourceFile(UastLintUtils.kt:735)←UElementVisitor$visitFile$3.run(UElementVisitor.kt:267)←LintClient.runReadAction(LintClient.kt:1700)←LintDriver$LintClientWrapper.runReadAction(LintDriver.kt:2867)←UElementVisitor.visitFile(UElementVisitor.kt:264)←LintDriver$visitUastDetectors$1.run(LintDriver.kt:2165)←LintClient.runReadAction(LintClient.kt:1700)←LintDriver$LintClientWrapper.runReadAction(LintDriver.kt:2867)←LintDriver.visitUastDetectors(LintDriver.kt:2165)←LintDriver.visitUast(LintDriver.kt:2127)←LintDriver.runFileDetectors(LintDriver.kt:1379)←LintDriver.checkProject(LintDriver.kt:1144)←LintDriver.checkProjectRoot(LintDriver.kt:615)←LintDriver.access$checkProjectRoot(LintDriver.kt:170)←LintDriver$analyzeOnly$1.invoke(LintDriver.kt:441)←LintDriver$analyzeOnly$1.invoke(LintDriver.kt:438)←LintDriver.doAnalyze(LintDriver.kt:497)←LintDriver.analyzeOnly(LintDriver.kt:438)←LintCliClient$analyzeOnly$1.invoke(LintCliClient.kt:237)←LintCliClient$analyzeOnly$1.invoke(LintCliClient.kt:237)←LintCliClient.run(LintCliClient.kt:279)←LintCliClient.run$default(LintCliClient.kt:262)←LintCliClient.analyzeOnly(LintCliClient.kt:237)←Main.run(Main.java:1687)←Main.run(Main.java:275)←NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)←NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)←DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)←Method.invoke(Method.java:568)←AndroidLintWorkAction.invokeLintMainRunMethod(AndroidLintWorkAction.kt:98)←AndroidLintWorkAction.runLint(AndroidLintWorkAction.kt:87)←AndroidLintWorkAction.execute(AndroidLintWorkAction.kt:62)←DefaultWorkerServer.execute(DefaultWorkerServer.java:63)←NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)←NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)←ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)←NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)←AbstractWorker$1.call(AbstractWorker.java:44)←AbstractWorker$1.call(AbstractWorker.java:41)←DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)←DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)←DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)←DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)←DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)←DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)←DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)←DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)←AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)←NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)←DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:169)←FutureTask.run(FutureTask.java:264)←DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)←DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)←DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)←Factories$1.create(Factories.java:31)←DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)←DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)←DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:114)←DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)←DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)←Executors$RunnableAdapter.call(Executors.java:539)←FutureTask.run(FutureTask.java:264)←ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)←ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)←ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)←ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)←Thread.run(Thread.java:833)`

You can run with --stacktrace or set environment variable `LINT_PRINT_STACKTRACE=true` to dump a full stacktrace to stdout.">
+ <location
+ file="src/main/java/androidx/constraintlayout/compose/demos/ChainsDemo.kt"/>
+ </issue>
+
+</issues>
diff --git a/constraintlayout/constraintlayout/api/api_lint.ignore b/constraintlayout/constraintlayout/api/api_lint.ignore
index 1f05e12..422e6ca 100644
--- a/constraintlayout/constraintlayout/api/api_lint.ignore
+++ b/constraintlayout/constraintlayout/api/api_lint.ignore
@@ -217,24 +217,6 @@
Invalid nullability on parameter `target` in method `onNestedFling`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.constraintlayout.motion.widget.MotionLayout#onNestedPreFling(android.view.View, float, float) parameter #0:
Invalid nullability on parameter `target` in method `onNestedPreFling`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.utils.widget.ImageFilterButton#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.utils.widget.ImageFilterView#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.utils.widget.MockView#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.utils.widget.MotionButton#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.utils.widget.MotionLabel#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.widget.ConstraintHelper#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.widget.Guideline#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.widget.Placeholder#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.constraintlayout.widget.ReactiveGuide#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
KotlinOperator: androidx.constraintlayout.motion.utils.ViewTimeCycle#get(float, long, android.view.View, androidx.constraintlayout.core.motion.utils.KeyCache):
@@ -349,6 +331,8 @@
Missing nullability on method `getSpans` return
MissingNullability: androidx.constraintlayout.helper.widget.Grid#init(android.util.AttributeSet) parameter #0:
Missing nullability on parameter `attrs` in method `init`
+MissingNullability: androidx.constraintlayout.helper.widget.Grid#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.helper.widget.Grid#setColumnWeights(String) parameter #0:
Missing nullability on parameter `columnWeights` in method `setColumnWeights`
MissingNullability: androidx.constraintlayout.helper.widget.Grid#setRowWeights(String) parameter #0:
@@ -1089,6 +1073,8 @@
Missing nullability on parameter `context` in method `ImageFilterButton`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterButton#ImageFilterButton(android.content.Context, android.util.AttributeSet, int) parameter #1:
Missing nullability on parameter `attrs` in method `ImageFilterButton`
+MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterButton#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterButton#setImageDrawable(android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `drawable` in method `setImageDrawable`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterView#ImageFilterView(android.content.Context) parameter #0:
@@ -1101,6 +1087,8 @@
Missing nullability on parameter `context` in method `ImageFilterView`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterView#ImageFilterView(android.content.Context, android.util.AttributeSet, int) parameter #1:
Missing nullability on parameter `attrs` in method `ImageFilterView`
+MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterView#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterView#setAltImageDrawable(android.graphics.drawable.Drawable) parameter #0:
Missing nullability on parameter `altDrawable` in method `setAltImageDrawable`
MissingNullability: androidx.constraintlayout.utils.widget.ImageFilterView#setImageDrawable(android.graphics.drawable.Drawable) parameter #0:
@@ -1117,6 +1105,8 @@
Missing nullability on parameter `attrs` in method `MockView`
MissingNullability: androidx.constraintlayout.utils.widget.MockView#mText:
Missing nullability on field `mText` in class `class androidx.constraintlayout.utils.widget.MockView`
+MissingNullability: androidx.constraintlayout.utils.widget.MockView#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.utils.widget.MotionButton#MotionButton(android.content.Context) parameter #0:
Missing nullability on parameter `context` in method `MotionButton`
MissingNullability: androidx.constraintlayout.utils.widget.MotionButton#MotionButton(android.content.Context, android.util.AttributeSet) parameter #0:
@@ -1127,6 +1117,8 @@
Missing nullability on parameter `context` in method `MotionButton`
MissingNullability: androidx.constraintlayout.utils.widget.MotionButton#MotionButton(android.content.Context, android.util.AttributeSet, int) parameter #1:
Missing nullability on parameter `attrs` in method `MotionButton`
+MissingNullability: androidx.constraintlayout.utils.widget.MotionButton#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#MotionLabel(android.content.Context) parameter #0:
Missing nullability on parameter `context` in method `MotionLabel`
MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#MotionLabel(android.content.Context, android.util.AttributeSet) parameter #0:
@@ -1135,6 +1127,8 @@
Missing nullability on parameter `context` in method `MotionLabel`
MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#getTypeface():
Missing nullability on method `getTypeface` return
+MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#setText(CharSequence) parameter #0:
Missing nullability on parameter `text` in method `setText`
MissingNullability: androidx.constraintlayout.utils.widget.MotionLabel#setTypeface(android.graphics.Typeface) parameter #0:
@@ -1149,6 +1143,8 @@
Missing nullability on parameter `context` in method `MotionTelltales`
MissingNullability: androidx.constraintlayout.utils.widget.MotionTelltales#MotionTelltales(android.content.Context, android.util.AttributeSet, int) parameter #1:
Missing nullability on parameter `attrs` in method `MotionTelltales`
+MissingNullability: androidx.constraintlayout.utils.widget.MotionTelltales#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.utils.widget.MotionTelltales#setText(CharSequence) parameter #0:
Missing nullability on parameter `text` in method `setText`
MissingNullability: androidx.constraintlayout.widget.Barrier#Barrier(android.content.Context) parameter #0:
@@ -1267,6 +1263,8 @@
Missing nullability on field `mReferenceTags` in class `class androidx.constraintlayout.widget.ConstraintHelper`
MissingNullability: androidx.constraintlayout.widget.ConstraintHelper#myContext:
Missing nullability on field `myContext` in class `class androidx.constraintlayout.widget.ConstraintHelper`
+MissingNullability: androidx.constraintlayout.widget.ConstraintHelper#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.widget.ConstraintHelper#removeView(android.view.View) parameter #0:
Missing nullability on parameter `view` in method `removeView`
MissingNullability: androidx.constraintlayout.widget.ConstraintHelper#resolveRtl(androidx.constraintlayout.core.widgets.ConstraintWidget, boolean) parameter #0:
@@ -1713,6 +1711,8 @@
Missing nullability on parameter `context` in method `Guideline`
MissingNullability: androidx.constraintlayout.widget.Guideline#Guideline(android.content.Context, android.util.AttributeSet, int, int) parameter #1:
Missing nullability on parameter `attrs` in method `Guideline`
+MissingNullability: androidx.constraintlayout.widget.Guideline#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.constraintlayout.widget.Placeholder#Placeholder(android.content.Context) parameter #0:
Missing nullability on parameter `context` in method `Placeholder`
MissingNullability: androidx.constraintlayout.widget.Placeholder#Placeholder(android.content.Context, android.util.AttributeSet) parameter #0:
@@ -1729,6 +1729,8 @@
Missing nullability on parameter `attrs` in method `Placeholder`
MissingNullability: androidx.constraintlayout.widget.Placeholder#getContent():
Missing nullability on method `getContent` return
+MissingNullability: androidx.constraintlayout.widget.Placeholder#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.constraintlayout.widget.Placeholder#updatePostMeasure(androidx.constraintlayout.widget.ConstraintLayout) parameter #0:
Missing nullability on parameter `container` in method `updatePostMeasure`
MissingNullability: androidx.constraintlayout.widget.Placeholder#updatePreLayout(androidx.constraintlayout.widget.ConstraintLayout) parameter #0:
@@ -1747,6 +1749,8 @@
Missing nullability on parameter `context` in method `ReactiveGuide`
MissingNullability: androidx.constraintlayout.widget.ReactiveGuide#ReactiveGuide(android.content.Context, android.util.AttributeSet, int, int) parameter #1:
Missing nullability on parameter `attrs` in method `ReactiveGuide`
+MissingNullability: androidx.constraintlayout.widget.ReactiveGuide#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.constraintlayout.widget.SharedValues#addListener(int, androidx.constraintlayout.widget.SharedValues.SharedValuesListener) parameter #1:
Missing nullability on parameter `listener` in method `addListener`
MissingNullability: androidx.constraintlayout.widget.SharedValues#removeListener(androidx.constraintlayout.widget.SharedValues.SharedValuesListener) parameter #0:
diff --git a/constraintlayout/constraintlayout/lint-baseline.xml b/constraintlayout/constraintlayout/lint-baseline.xml
index 6f8af38..a9b5204 100644
--- a/constraintlayout/constraintlayout/lint-baseline.xml
+++ b/constraintlayout/constraintlayout/lint-baseline.xml
@@ -5332,6 +5332,15 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected void dispatchDraw(Canvas canvas) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java"/>
+ </issue>
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public void setOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -9247,6 +9256,15 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected void dispatchDraw(Canvas canvas) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java"/>
+ </issue>
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public void setScene(MotionScene scene) {"
errorLine2=" ~~~~~~~~~~~">
<location
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
index c4eda2a..577a8d7 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
@@ -61,29 +61,30 @@
* and {@link ConstraintSet#setGuidelinePercent} functions in {@link ConstraintSet}.
* <p>
* Example of a {@code Button} constrained to a vertical {@code Guideline}:
- * <pre>
- * <androidx.constraintlayout.widget.ConstraintLayout
- * xmlns:android="http://schemas.android.com/apk/res/android"
- * xmlns:app="http://schemas.android.com/apk/res-auto"
- * xmlns:tools="http://schemas.android.com/tools"
- * android:layout_width="match_parent"
- * android:layout_height="match_parent">
+ * <pre>{@code
+ * <androidx.constraintlayout.widget.ConstraintLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * xmlns:tools="http://schemas.android.com/tools"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent">
*
- * <androidx.constraintlayout.widget.Guideline
- * android:layout_width="wrap_content"
- * android:layout_height="wrap_content"
- * android:id="@+id/guideline"
- * app:layout_constraintGuide_begin="100dp"
- * android:orientation="vertical"/>
- * <Button
- * android:text="Button"
- * android:layout_width="wrap_content"
- * android:layout_height="wrap_content"
- * android:id="@+id/button"
- * app:layout_constraintLeft_toLeftOf="@+id/guideline"
- * android:layout_marginTop="16dp"
- * app:layout_constraintTop_toTopOf="parent" />
- * </androidx.constraintlayout.widget.ConstraintLayout>
+ * <androidx.constraintlayout.widget.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/guideline"
+ * app:layout_constraintGuide_begin="100dp"
+ * android:orientation="vertical"/>
+ * <Button
+ * android:text="Button"
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/button"
+ * app:layout_constraintLeft_toLeftOf="@+id/guideline"
+ * android:layout_marginTop="16dp"
+ * app:layout_constraintTop_toTopOf="parent" />
+ * </androidx.constraintlayout.widget.ConstraintLayout>
+ * }
* </pre>
* <p/>
*/
diff --git a/coordinatorlayout/coordinatorlayout/api/api_lint.ignore b/coordinatorlayout/coordinatorlayout/api/api_lint.ignore
index f200680..06d3c6e 100644
--- a/coordinatorlayout/coordinatorlayout/api/api_lint.ignore
+++ b/coordinatorlayout/coordinatorlayout/api/api_lint.ignore
@@ -1,6 +1,4 @@
// Baseline format: 1.0
-InvalidNullabilityOverride: androidx.coordinatorlayout.widget.CoordinatorLayout#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.coordinatorlayout.widget.CoordinatorLayout#onNestedPreScroll(android.view.View, int, int, int[]) parameter #0:
Invalid nullability on parameter `target` in method `onNestedPreScroll`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.coordinatorlayout.widget.CoordinatorLayout#onNestedPreScroll(android.view.View, int, int, int[]) parameter #3:
@@ -35,6 +33,8 @@
Missing nullability on method `generateLayoutParams` return
MissingNullability: androidx.coordinatorlayout.widget.CoordinatorLayout#generateLayoutParams(android.view.ViewGroup.LayoutParams) parameter #0:
Missing nullability on parameter `p` in method `generateLayoutParams`
+MissingNullability: androidx.coordinatorlayout.widget.CoordinatorLayout#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `onDraw`
MissingNullability: androidx.coordinatorlayout.widget.CoordinatorLayout#onInterceptTouchEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `ev` in method `onInterceptTouchEvent`
MissingNullability: androidx.coordinatorlayout.widget.CoordinatorLayout#onNestedFling(android.view.View, float, float, boolean) parameter #0:
diff --git a/coordinatorlayout/coordinatorlayout/lint-baseline.xml b/coordinatorlayout/coordinatorlayout/lint-baseline.xml
index eb4748e..b3144af 100644
--- a/coordinatorlayout/coordinatorlayout/lint-baseline.xml
+++ b/coordinatorlayout/coordinatorlayout/lint-baseline.xml
@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha08)" variant="all" version="7.4.0-alpha08">
+<issues format="6" by="lint 8.0.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-alpha07)" variant="all" version="8.0.0-alpha07">
+
+ <issue
+ id="UnknownNullness"
+ message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
+ errorLine1=" protected boolean drawChild(Canvas canvas, View child, long drawingTime) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java"/>
+ </issue>
<issue
id="UnknownNullness"
diff --git a/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java b/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
index 5da3a24..520c8ee 100644
--- a/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
+++ b/core/core-appdigest/src/androidTest/java/androidx/core/appdigest/ChecksumsTest.java
@@ -214,8 +214,8 @@
}
private static String executeShellCommand(String command) throws IOException {
- if (Build.VERSION.SDK_INT >= 24) {
- return InstallerApi24.executeShellCommand(command);
+ if (Build.VERSION.SDK_INT >= 29) {
+ return InstallerApi29.executeShellCommand(command);
}
return "";
}
@@ -1022,8 +1022,8 @@
}
void installSplits(String[] names) throws Exception {
- if (Build.VERSION.SDK_INT >= 24) {
- new InstallerApi24(mContext).installSplits(names);
+ if (Build.VERSION.SDK_INT >= 29) {
+ new InstallerApi29(mContext).installSplits(names);
}
}
@@ -1035,17 +1035,17 @@
}
private boolean isAppInstalled(String packageName) throws IOException {
- if (Build.VERSION.SDK_INT >= 24) {
- return InstallerApi24.isAppInstalled(packageName);
+ if (Build.VERSION.SDK_INT >= 29) {
+ return InstallerApi29.isAppInstalled(packageName);
}
return false;
}
- @RequiresApi(24)
- static class InstallerApi24 {
+ @RequiresApi(29)
+ static class InstallerApi29 {
protected Context mContext;
- InstallerApi24(Context context) {
+ InstallerApi29(Context context) {
mContext = context;
}
@@ -1120,7 +1120,7 @@
final String action = "androidx.core.appdigest.COMMIT_COMPLETE." + resultId;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(action);
- mContext.registerReceiver(broadcastReceiver, intentFilter);
+ mContext.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
Intent intent = new Intent(action);
PendingIntent sender = PendingIntent.getBroadcast(mContext, resultId, intent,
@@ -1139,7 +1139,7 @@
}
@RequiresApi(31)
- static class InstallerApi31 extends InstallerApi24 {
+ static class InstallerApi31 extends InstallerApi29 {
InstallerApi31(Context context) {
super(context);
}
diff --git a/core/core-ktx/src/main/java/androidx/core/os/OutcomeReceiver.kt b/core/core-ktx/src/main/java/androidx/core/os/OutcomeReceiver.kt
index 74052a3..d6698aa 100644
--- a/core/core-ktx/src/main/java/androidx/core/os/OutcomeReceiver.kt
+++ b/core/core-ktx/src/main/java/androidx/core/os/OutcomeReceiver.kt
@@ -61,7 +61,7 @@
private class ContinuationOutcomeReceiver<R, E : Throwable>(
private val continuation: Continuation<R>
) : OutcomeReceiver<R, E>, AtomicBoolean(false) {
- override fun onResult(result: R & Any) {
+ override fun onResult(result: R) {
// Do not attempt to resume more than once, even if the caller of the returned
// OutcomeReceiver is buggy and tries anyway.
if (compareAndSet(false, true)) {
diff --git a/core/core-location-altitude-proto/OWNERS b/core/core-location-altitude-proto/OWNERS
new file mode 100644
index 0000000..b95c469
--- /dev/null
+++ b/core/core-location-altitude-proto/OWNERS
@@ -0,0 +1,2 @@
+bjj@google.com
+sooniln@google.com
diff --git a/core/core-location-altitude-proto/build.gradle b/core/core-location-altitude-proto/build.gradle
new file mode 100644
index 0000000..ee2c418
--- /dev/null
+++ b/core/core-location-altitude-proto/build.gradle
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("java-library")
+ id("com.google.protobuf")
+}
+
+dependencies {
+ implementation(libs.protobufLite)
+}
+
+protobuf {
+ protoc {
+ artifact = libs.protobufCompiler.get()
+ }
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java {
+ option "lite"
+ }
+ }
+ }
+ }
+}
+
+afterEvaluate {
+ lint {
+ lintOptions {
+ // protobuf generates unannotated and synthetic accessor methods
+ disable("UnknownNullness", "SyntheticAccessor")
+ }
+ }
+}
+
+def preferencesProtoJarJarTask = tasks.register("exportJar", Jar) {
+ archiveBaseName.set("export")
+ from(sourceSets.main.output)
+ // The proto-lite dependency includes .proto files, which are not used by the altitude
+ // compatibility library. When apps depend on this library as well as proto-lite directly, these
+ // files conflict since jarjar only renames the java classes. Remove them here since they are
+ // unused.
+ exclude("**/*.proto")
+
+ from(zipTree(configurations.detachedConfiguration(
+ dependencies.create(libs.protobufLite.get())).getSingleFile()))
+}
+
+def jarjarConf = configurations.register("export")
+artifacts.add(jarjarConf.name, preferencesProtoJarJarTask.flatMap { it.archiveFile })
+
+androidx {
+ name = "Location Altitude Compatibility Library Protos"
+ publish = Publish.NONE
+ inceptionYear = "2022"
+ description = "Implementation protos for core-location-altitude artifact."
+}
diff --git a/core/core-location-altitude-proto/src/main/proto/map_params.proto b/core/core-location-altitude-proto/src/main/proto/map_params.proto
new file mode 100644
index 0000000..9082582
--- /dev/null
+++ b/core/core-location-altitude-proto/src/main/proto/map_params.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package androidx.core.location.altitude.impl.proto;
+
+option java_package = "androidx.core.location.altitude.impl.proto";
+option java_multiple_files = true;
+
+// Defines parameters for a spherically projected geoid map and corresponding
+// tile management.
+message MapParamsProto {
+ // Defines the resolution of the map in terms of an S2 level.
+ optional int32 map_s2_level = 1;
+ // Defines the resolution of the tiles in cache in terms of an S2 level.
+ optional int32 cache_tile_s2_level = 2;
+ // Defines the resolution of the tiles on disk in terms of an S2 level.
+ optional int32 disk_tile_s2_level = 3;
+ // Defines the `a` coefficient in the expression `a * map_value + b` used to
+ // calculate a geoid height in meters.
+ optional double model_a_meters = 4;
+ // Defines the `b` coefficient in the expression `a * map_value + b` used to
+ // calculate a geoid height in meters.
+ optional double model_b_meters = 5;
+ // Defines the root mean square error in meters of the geoid height.
+ optional double model_rmse_meters = 6;
+}
diff --git a/core/core-location-altitude-proto/src/main/proto/s2_tile.proto b/core/core-location-altitude-proto/src/main/proto/s2_tile.proto
new file mode 100644
index 0000000..1b6ce41
--- /dev/null
+++ b/core/core-location-altitude-proto/src/main/proto/s2_tile.proto
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package androidx.core.location.altitude.impl.proto;
+
+option java_package = "androidx.core.location.altitude.impl.proto";
+option java_multiple_files = true;
+
+// A single tile associating values in the unit interval [0, 1] to map cells.
+message S2TileProto {
+ // The S2 token associated with the common parent of all map cells in this
+ // tile.
+ optional string tile_key = 1;
+
+ // Encoded data that merge into a value in the unit interval [0, 1] for each
+ // map cell in this tile.
+ optional bytes byte_buffer = 2;
+ optional bytes byte_jpeg = 3;
+ optional bytes byte_png = 4;
+}
\ No newline at end of file
diff --git a/core/core-location-altitude/api/current.txt b/core/core-location-altitude/api/current.txt
index e6f50d0..9263ab2 100644
--- a/core/core-location-altitude/api/current.txt
+++ b/core/core-location-altitude/api/current.txt
@@ -1 +1,9 @@
// Signature format: 4.0
+package androidx.core.location.altitude {
+
+ public final class AltitudeConverterCompat {
+ method @WorkerThread public static void addMslAltitudeToLocation(android.content.Context, android.location.Location) throws java.io.IOException;
+ }
+
+}
+
diff --git a/core/core-location-altitude/api/restricted_current.txt b/core/core-location-altitude/api/restricted_current.txt
index e6f50d0..9263ab2 100644
--- a/core/core-location-altitude/api/restricted_current.txt
+++ b/core/core-location-altitude/api/restricted_current.txt
@@ -1 +1,9 @@
// Signature format: 4.0
+package androidx.core.location.altitude {
+
+ public final class AltitudeConverterCompat {
+ method @WorkerThread public static void addMslAltitudeToLocation(android.content.Context, android.location.Location) throws java.io.IOException;
+ }
+
+}
+
diff --git a/core/core-location-altitude/build.gradle b/core/core-location-altitude/build.gradle
index ccee9b7..b5b4820 100644
--- a/core/core-location-altitude/build.gradle
+++ b/core/core-location-altitude/build.gradle
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+import androidx.build.BundleInsideHelper
import androidx.build.LibraryType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("AndroidXPlugin")
@@ -24,18 +26,34 @@
android {
namespace "androidx.core.location.altitude"
+
+ buildTypes.all {
+ consumerProguardFiles "proguard-rules.pro"
+ }
}
+BundleInsideHelper.forInsideAar(
+ project,
+ /* from = */ "com.google.protobuf",
+ /* to = */ "androidx.core.location.altitude.impl.proto"
+)
+
dependencies {
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.5.0")
- implementation("androidx.concurrent:concurrent-futures:1.1.0")
+ bundleInside(project(path: ":core:core-location-altitude-proto", configuration: "export"))
+
+ implementation(libs.autoValueAnnotations)
implementation("androidx.core:core:1.9.0")
+ implementation("androidx.room:room-runtime:2.4.3")
+
+ annotationProcessor(libs.autoValue)
+ annotationProcessor("androidx.room:room-compiler:2.4.3")
androidTestImplementation(libs.junit)
- androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.truth)
}
diff --git a/core/core-location-altitude/proguard-rules.pro b/core/core-location-altitude/proguard-rules.pro
new file mode 100644
index 0000000..dee5477
--- /dev/null
+++ b/core/core-location-altitude/proguard-rules.pro
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# libproto uses reflection to deserialize a Proto, which Proguard can't accurately detect.
+# Keep all the class members of any generated messages to ensure we can deserialize properly inside
+# these classes.
+-if class * extends androidx.core.location.altitude.impl.proto.GeneratedMessageLite
+-keepclasseswithmembers
\ No newline at end of file
diff --git a/core/core-location-altitude/src/androidTest/java/androidx/core/location/altitude/AltitudeConverterCompatTest.java b/core/core-location-altitude/src/androidTest/java/androidx/core/location/altitude/AltitudeConverterCompatTest.java
index 3c61ed9..7b0787d 100644
--- a/core/core-location-altitude/src/androidTest/java/androidx/core/location/altitude/AltitudeConverterCompatTest.java
+++ b/core/core-location-altitude/src/androidTest/java/androidx/core/location/altitude/AltitudeConverterCompatTest.java
@@ -23,138 +23,143 @@
import android.content.Context;
import android.location.Location;
-import androidx.arch.core.util.Function;
import androidx.core.location.LocationCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-@SmallTest
+import java.io.IOException;
+
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class AltitudeConverterCompatTest {
- private Location mValidLocation;
private Context mContext;
- private static void assertEquals(Location actual, Location expected) {
- assertThat(actual.getLatitude()).isEqualTo(expected.getLatitude());
- assertThat(actual.getLongitude()).isEqualTo(expected.getLongitude());
- assertEquals(actual, expected, Location::hasAltitude, Location::getAltitude);
- assertEquals(
- actual,
- expected,
- LocationCompat::hasVerticalAccuracy,
- LocationCompat::getVerticalAccuracyMeters);
- assertEquals(
- actual, expected, LocationCompat::hasMslAltitude,
- LocationCompat::getMslAltitudeMeters);
- assertEquals(
- actual,
- expected,
- LocationCompat::hasMslAltitudeAccuracy,
- LocationCompat::getMslAltitudeAccuracyMeters);
- }
-
- private static <T> void assertEquals(
- Location actual,
- Location expected,
- Function<Location, Boolean> has,
- Function<Location, T> get) {
- assertThat(has.apply(actual)).isEqualTo(has.apply(expected));
- if (has.apply(expected)) {
- assertThat(get.apply(actual)).isEqualTo(get.apply(expected));
- }
- }
-
@Before
public void setUp() {
- mValidLocation = new Location("");
- mValidLocation.setLatitude(-90);
- mValidLocation.setLongitude(180);
- mValidLocation.setAltitude(-1);
- LocationCompat.setVerticalAccuracyMeters(mValidLocation, 1);
-
mContext = ApplicationProvider.getApplicationContext();
}
@Test
- public void testAddMslAltitude_validLocationThrows() {
- assertThrows(
- UnsupportedOperationException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(mValidLocation)));
+ public void testAddMslAltitudeToLocation_expectedBehavior() throws IOException {
+ // Interpolates in boundary region (bffffc).
+ Location location = new Location("");
+ location.setLatitude(-35.334815);
+ location.setLongitude(-45);
+ location.setAltitude(-1);
+ LocationCompat.setVerticalAccuracyMeters(location, 1);
+ // Loads data from raw assets.
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(5.0622);
+ assertThat(LocationCompat.getMslAltitudeAccuracyMeters(location)).isGreaterThan(1f);
+ assertThat(LocationCompat.getMslAltitudeAccuracyMeters(location)).isLessThan(1.1f);
+
+ // Again interpolates at same location to assert no loading from raw assets. Also checks
+ // behavior w.r.t. invalid vertical accuracy.
+ location = new Location("");
+ location.setLatitude(-35.334815);
+ location.setLongitude(-45);
+ location.setAltitude(-1);
+ LocationCompat.setVerticalAccuracyMeters(location, -1); // Invalid vertical accuracy
+ // Results in same outcome.
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(5.0622);
+ assertThat(LocationCompat.hasMslAltitudeAccuracy(location)).isFalse();
+
+ // Interpolates out of boundary region, e.g., Hawaii.
+ location = new Location("");
+ location.setLatitude(19.545519);
+ location.setLongitude(-155.998774);
+ location.setAltitude(-1);
+ LocationCompat.setVerticalAccuracyMeters(location, 1);
+ // Loads data from raw assets.
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(-19.2359);
+ assertThat(LocationCompat.getMslAltitudeAccuracyMeters(location)).isGreaterThan(1f);
+ assertThat(LocationCompat.getMslAltitudeAccuracyMeters(location)).isLessThan(1.1f);
+
+ // The following round out test coverage for boundary regions.
+
+ location = new Location("");
+ location.setLatitude(-35.229154);
+ location.setLongitude(44.925335);
+ location.setAltitude(-1);
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(-34.1913);
+
+ location = new Location("");
+ location.setLatitude(-35.334815);
+ location.setLongitude(45);
+ location.setAltitude(-1);
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(-34.2258);
+
+ location = new Location("");
+ location.setLatitude(35.229154);
+ location.setLongitude(-44.925335);
+ location.setAltitude(-1);
+ AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location);
+ assertThat(LocationCompat.getMslAltitudeMeters(location)).isWithin(2).of(-11.0691);
}
@Test
- public void testAddMslAltitude_invalidLatitudeThrows() {
- Location location = new Location(mValidLocation);
+ public void testAddMslAltitudeToLocation_invalidLatitudeThrows() {
+ Location location = new Location("");
+ location.setLongitude(-44.962683);
+ location.setAltitude(-1);
location.setLatitude(Double.NaN);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setLatitude(91);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setLatitude(-91);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
}
@Test
- public void testAddMslAltitude_invalidLongitudeThrows() {
- Location location = new Location(mValidLocation);
+ public void testAddMslAltitudeToLocation_invalidLongitudeThrows() {
+ Location location = new Location("");
+ location.setLatitude(-35.246789);
+ location.setAltitude(-1);
location.setLongitude(Double.NaN);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setLongitude(181);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setLongitude(-181);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
}
@Test
- public void testAddMslAltitude_invalidAltitudeThrows() {
- Location location = new Location(mValidLocation);
+ public void testAddMslAltitudeToLocation_invalidAltitudeThrows() {
+ Location location = new Location("");
+ location.setLatitude(-35.246789);
+ location.setLongitude(-44.962683);
- location.removeAltitude();
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setAltitude(Double.NaN);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
location.setAltitude(Double.POSITIVE_INFINITY);
- assertThrows(
- IllegalArgumentException.class,
- () -> AltitudeConverterCompat.addMslAltitudeAsync(mContext,
- new Location(location)));
+ assertThrows(IllegalArgumentException.class,
+ () -> AltitudeConverterCompat.addMslAltitudeToLocation(mContext, location));
}
}
diff --git a/core/core-location-altitude/src/main/assets/database/geoid-height-map-v0.db b/core/core-location-altitude/src/main/assets/database/geoid-height-map-v0.db
new file mode 100644
index 0000000..1037857
--- /dev/null
+++ b/core/core-location-altitude/src/main/assets/database/geoid-height-map-v0.db
Binary files differ
diff --git a/core/core-location-altitude/src/main/assets/database/geoids-v0.db b/core/core-location-altitude/src/main/assets/database/geoids-v0.db
deleted file mode 100644
index 1be3bd7..0000000
--- a/core/core-location-altitude/src/main/assets/database/geoids-v0.db
+++ /dev/null
Binary files differ
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
index f3cd443..555aaa0 100644
--- a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
@@ -18,74 +18,99 @@
import android.content.Context;
import android.location.Location;
+import android.os.Build;
+import androidx.annotation.DoNotInline;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.core.util.Preconditions;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
+import androidx.core.location.altitude.impl.AltitudeConverter;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Objects;
+import java.io.IOException;
/**
* Converts altitudes reported above the World Geodetic System 1984 (WGS84) reference ellipsoid
* into ones above Mean Sea Level.
*
+ * <p>Reference:
+ *
+ * <pre>
+ * Brian Julian and Michael Angermann.
+ * "Resource efficient and accurate altitude conversion to Mean Sea Level."
+ * 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS).
+ * </pre>
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class AltitudeConverterCompat {
- private static final double MAX_ABS_VALID_LATITUDE = 90;
- private static final double MAX_ABS_VALID_LONGITUDE = 180;
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ @Nullable
+ private static AltitudeConverter sAltitudeConverter;
/** Prevents instantiation. */
private AltitudeConverterCompat() {
}
/**
- * Returns a {@link ListenableFuture} that, upon success, adds a Mean Sea Level altitude to the
- * {@code location} in {@link Location#getExtras()}. In addition, adds a Mean Sea Level altitude
- * accuracy if the {@code location} has a valid vertical accuracy.
+ * Adds a Mean Sea Level altitude to the {@code location}. In addition, adds a Mean Sea Level
+ * altitude accuracy if the {@code location} has a finite and non-negative vertical accuracy;
+ * otherwise, does not add a corresponding accuracy.
*
- * <p>The {@link ListenableFuture} leaves the {@code location} unchanged if and only if any
- * of the following are true:
+ * <p>Must be called off the main thread as data may be loaded from raw assets.
*
- * <ul>
- * <li>the {@code location} has an invalid latitude, longitude, or altitude above WGS84
- * <li>an I/O error occurs when loading data from raw assets via {@code context}.
- * </ul>
- *
- * <p>NOTE: Currently throws {@link UnsupportedOperationException} for a valid {@code location}.
- *
+ * @throws IOException if an I/O error occurs when loading data from raw assets.
+ * @throws IllegalArgumentException if the {@code location} has an invalid latitude, longitude,
+ * or altitude above WGS84. Specifically, the latitude must be
+ * between -90 and 90 (both inclusive), the longitude must be
+ * between -180 and 180 (both inclusive), and the altitude
+ * above WGS84 must be finite.
*/
- @NonNull
- public static ListenableFuture<Location> addMslAltitudeAsync(
- @NonNull Context context,
- @NonNull Location location) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(location);
- validate(location);
- throw new UnsupportedOperationException("addMslAltitude method not implemented.");
+ @WorkerThread
+ public static void addMslAltitudeToLocation(@NonNull Context context,
+ @NonNull Location location) throws IOException {
+ if (Build.VERSION.SDK_INT >= 34) {
+ Api34Impl.addMslAltitudeToLocation(context, location);
+ return;
+ }
+
+ AltitudeConverter altitudeConverter;
+ synchronized (sLock) {
+ if (sAltitudeConverter == null) {
+ sAltitudeConverter = new AltitudeConverter();
+ }
+ altitudeConverter = sAltitudeConverter;
+ }
+ altitudeConverter.addMslAltitudeToLocation(context, location);
}
- /**
- * Throws an {@link IllegalArgumentException} if the {@code location} has an invalid latitude,
- * longitude, or altitude above WGS84.
- */
- private static void validate(@NonNull Location location) {
- Preconditions.checkArgument(isFiniteAndAtAbsMost(location.getLatitude(),
- MAX_ABS_VALID_LATITUDE), "Location must contain a valid latitude.");
- Preconditions.checkArgument(isFiniteAndAtAbsMost(location.getLongitude(),
- MAX_ABS_VALID_LONGITUDE), "Location must contain a valid longitude.");
- Preconditions.checkArgument(location.hasAltitude() && isFinite(location.getAltitude()),
- "Location must contain a valid altitude above WGS84.");
- }
+ @RequiresApi(34)
+ private static class Api34Impl {
- private static boolean isFiniteAndAtAbsMost(double value, double rhs) {
- return isFinite(value) && Math.abs(value) <= rhs;
- }
+ private static final Object sLock = new Object();
- private static boolean isFinite(double value) {
- return !Double.isInfinite(value) && !Double.isNaN(value);
+ @GuardedBy("sLock")
+ @Nullable
+ private static Object sAltitudeConverter;
+
+ /** Prevents instantiation. */
+ private Api34Impl() {
+ }
+
+ @DoNotInline
+ static void addMslAltitudeToLocation(@NonNull Context context,
+ @NonNull Location location) throws IOException {
+ android.location.altitude.AltitudeConverter altitudeConverter;
+ synchronized (sLock) {
+ if (sAltitudeConverter == null) {
+ sAltitudeConverter = new android.location.altitude.AltitudeConverter();
+ }
+ altitudeConverter =
+ (android.location.altitude.AltitudeConverter) sAltitudeConverter;
+ }
+ altitudeConverter.addMslAltitudeToLocation(context, location);
+ }
}
}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/AltitudeConverter.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/AltitudeConverter.java
new file mode 100644
index 0000000..3fd1272
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/AltitudeConverter.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl;
+
+import android.content.Context;
+import android.location.Location;
+
+import androidx.annotation.NonNull;
+import androidx.core.location.LocationCompat;
+import androidx.core.location.altitude.impl.proto.MapParamsProto;
+import androidx.core.util.Preconditions;
+
+import java.io.IOException;
+
+/** Implements {@link androidx.core.location.altitude.AltitudeConverterCompat}. */
+public final class AltitudeConverter {
+
+ private static final double MAX_ABS_VALID_LATITUDE = 90;
+ private static final double MAX_ABS_VALID_LONGITUDE = 180;
+
+ /** Manages a mapping of geoid heights associated with S2 cells. */
+ private final GeoidHeightMap mGeoidHeightMap = new GeoidHeightMap();
+
+ /**
+ * Creates an instance that manages an independent cache to optimized conversions of locations
+ * in proximity to one another.
+ */
+ public AltitudeConverter() {
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the {@code location} has an invalid latitude,
+ * longitude, or altitude above WGS84.
+ */
+ private static void validate(@NonNull Location location) {
+ Preconditions.checkArgument(
+ isFiniteAndAtAbsMost(location.getLatitude(), MAX_ABS_VALID_LATITUDE),
+ "Invalid latitude: %f", location.getLatitude());
+ Preconditions.checkArgument(
+ isFiniteAndAtAbsMost(location.getLongitude(), MAX_ABS_VALID_LONGITUDE),
+ "Invalid longitude: %f", location.getLongitude());
+ Preconditions.checkArgument(location.hasAltitude(), "Missing altitude above WGS84");
+ Preconditions.checkArgument(Double.isFinite(location.getAltitude()),
+ "Invalid altitude above WGS84: %f", location.getAltitude());
+ }
+
+ private static boolean isFiniteAndAtAbsMost(double value, double rhs) {
+ return isFinite(value) && Math.abs(value) <= rhs;
+ }
+
+ private static boolean isFinite(double value) {
+ return !Double.isInfinite(value) && !Double.isNaN(value);
+ }
+
+ /**
+ * Returns the four S2 cell IDs for the map square associated with the {@code location}.
+ *
+ * <p>The first map cell, denoted z11 in the appendix of the referenced paper below, contains
+ * the location. The others are the map cells denoted z21, z12, and z22, in that order.
+ *
+ * <p>Reference:
+ *
+ * <pre>
+ * Brian Julian and Michael Angermann.
+ * "Resource efficient and accurate altitude conversion to Mean Sea Level."
+ * 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS).
+ * </pre>
+ */
+ @NonNull
+ private static long[] findMapSquare(@NonNull MapParamsProto params,
+ @NonNull Location location) {
+ long s2CellId =
+ S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), location.getLongitude());
+
+ // Cell-space properties and coordinates.
+ int sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.getMapS2Level());
+ int maxIj = 1 << S2CellIdUtils.MAX_LEVEL;
+ long z11 = S2CellIdUtils.getParent(s2CellId, params.getMapS2Level());
+ int f11 = S2CellIdUtils.getFace(s2CellId);
+ int i1 = S2CellIdUtils.getI(s2CellId);
+ int j1 = S2CellIdUtils.getJ(s2CellId);
+ int i2 = i1 + sizeIj;
+ int j2 = j1 + sizeIj;
+
+ // Non-boundary region calculation - simplest and most common case.
+ if (i2 < maxIj && j2 < maxIj) {
+ return new long[]{
+ z11,
+ S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j1),
+ params.getMapS2Level()),
+ S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i1, j2),
+ params.getMapS2Level()),
+ S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j2),
+ params.getMapS2Level())
+ };
+ }
+
+ // Boundary region calculation
+ long[] edgeNeighbors = new long[4];
+ S2CellIdUtils.getEdgeNeighbors(z11, edgeNeighbors);
+ long z11W = edgeNeighbors[0];
+ long z11S = edgeNeighbors[1];
+ long z11E = edgeNeighbors[2];
+ long z11N = edgeNeighbors[3];
+
+ long[] otherEdgeNeighbors = new long[4];
+ S2CellIdUtils.getEdgeNeighbors(z11W, otherEdgeNeighbors);
+ S2CellIdUtils.getEdgeNeighbors(z11S, edgeNeighbors);
+ long z11SW = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+ S2CellIdUtils.getEdgeNeighbors(z11E, otherEdgeNeighbors);
+ long z11SE = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+ S2CellIdUtils.getEdgeNeighbors(z11N, edgeNeighbors);
+ long z11NE = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+
+ long z21 = (f11 % 2 == 1 && i2 >= maxIj) ? z11SW : z11S;
+ long z12 = (f11 % 2 == 0 && j2 >= maxIj) ? z11NE : z11E;
+ long z22 = (z21 == z11SW) ? z11S : (z12 == z11NE) ? z11E : z11SE;
+
+ // Reuse edge neighbors' array to avoid an extra allocation.
+ edgeNeighbors[0] = z11;
+ edgeNeighbors[1] = z21;
+ edgeNeighbors[2] = z12;
+ edgeNeighbors[3] = z22;
+ return edgeNeighbors;
+ }
+
+ /**
+ * Returns the first common non-z11 neighbor found between the two arrays of edge neighbors. If
+ * such a common neighbor does not exist, returns z11.
+ */
+ private static long findCommonNeighbor(
+ @NonNull long[] edgeNeighbors, @NonNull long[] otherEdgeNeighbors, long z11) {
+ for (long edgeNeighbor : edgeNeighbors) {
+ if (edgeNeighbor == z11) {
+ continue;
+ }
+ for (long otherEdgeNeighbor : otherEdgeNeighbors) {
+ if (edgeNeighbor == otherEdgeNeighbor) {
+ return edgeNeighbor;
+ }
+ }
+ }
+ return z11;
+ }
+
+ /**
+ * Adds to {@code location} the bilinearly interpolated Mean Sea Level altitude. In addition, a
+ * Mean Sea Level altitude accuracy is added if the {@code location} has a valid vertical
+ * accuracy; otherwise, does not add a corresponding accuracy.
+ */
+ private static void addMslAltitude(@NonNull MapParamsProto params,
+ @NonNull double[] geoidHeightsMeters, @NonNull Location location) {
+ double h0 = geoidHeightsMeters[0];
+ double h1 = geoidHeightsMeters[1];
+ double h2 = geoidHeightsMeters[2];
+ double h3 = geoidHeightsMeters[3];
+
+ // Bilinear interpolation on an S2 square of size equal to that of a map cell. wi and wj
+ // are the normalized [0,1] weights in the i and j directions, respectively, allowing us to
+ // employ the simplified unit square formulation.
+ long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(),
+ location.getLongitude());
+ double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.getMapS2Level());
+ double wi = (S2CellIdUtils.getI(s2CellId) % sizeIj) / sizeIj;
+ double wj = (S2CellIdUtils.getJ(s2CellId) % sizeIj) / sizeIj;
+ double offsetMeters = h0 + (h1 - h0) * wi + (h2 - h0) * wj + (h3 - h1 - h2 + h0) * wi * wj;
+
+ LocationCompat.setMslAltitudeMeters(location, location.getAltitude() - offsetMeters);
+ if (LocationCompat.hasVerticalAccuracy(location)) {
+ double verticalAccuracyMeters = LocationCompat.getVerticalAccuracyMeters(location);
+ if (isFinite(verticalAccuracyMeters) && verticalAccuracyMeters >= 0) {
+ LocationCompat.setMslAltitudeAccuracyMeters(location,
+ (float) Math.hypot(verticalAccuracyMeters, params.getModelRmseMeters()));
+ }
+ }
+ }
+
+ /**
+ * Implements
+ * {@link androidx.core.location.altitude.AltitudeConverterCompat#addMslAltitudeToLocation(Context, Location)}.
+ */
+ public void addMslAltitudeToLocation(@NonNull Context context, @NonNull Location location)
+ throws IOException {
+ validate(location);
+ MapParamsProto params = GeoidHeightMap.getParams(context);
+ long[] s2CellIds = findMapSquare(params, location);
+ double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, context, s2CellIds);
+ addMslAltitude(params, geoidHeightsMeters, location);
+ }
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/GeoidHeightMap.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/GeoidHeightMap.java
new file mode 100644
index 0000000..811a19f
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/GeoidHeightMap.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.LruCache;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.location.altitude.impl.db.AltitudeConverterDatabase;
+import androidx.core.location.altitude.impl.db.MapParamsEntity;
+import androidx.core.location.altitude.impl.db.TilesEntity;
+import androidx.core.location.altitude.impl.proto.ByteString;
+import androidx.core.location.altitude.impl.proto.MapParamsProto;
+import androidx.core.location.altitude.impl.proto.S2TileProto;
+import androidx.core.util.Preconditions;
+import androidx.room.Room;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * Manages a mapping of geoid heights associated with S2 cells, referred to as MAP CELLS.
+ *
+ * <p>Tiles are used extensively to reduce the number of entries needed to be stored in memory and
+ * on disk. A tile associates geoid heights with all map cells of a common parent at a specified S2
+ * level.
+ *
+ * <p>Since bilinear interpolation considers at most four map cells at a time, at most four tiles
+ * are simultaneously stored in memory. These tiles, referred to as CACHE TILES, are each keyed by
+ * its common parent's S2 cell ID, referred to as a CACHE KEY.
+ *
+ * <p>Absent cache tiles needed for interpolation are constructed from larger tiles stored on disk.
+ * The latter tiles, referred to as DISK TILES, are each keyed by its common parent's S2 cell token,
+ * referred to as a DISK TOKEN.
+ */
+final class GeoidHeightMap {
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ @Nullable
+ private static MapParamsProto sParams;
+
+ /** Defines the resource database for {@link AltitudeConverter}. */
+ @GuardedBy("sLock")
+ @Nullable
+ private static AltitudeConverterDatabase sDatabase;
+
+
+ /** Defines a cache large enough to hold all cache tiles needed for interpolation. */
+ private final LruCache<Long, S2TileProto> mCacheTiles = new LruCache<>(4);
+
+ @NonNull
+ public static AltitudeConverterDatabase getDatabase(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sDatabase == null) {
+ sDatabase = Room.databaseBuilder(context.getApplicationContext(),
+ AltitudeConverterDatabase.class, "geoid-height-map.db").createFromAsset(
+ "database/geoid-height-map-v0.db").build();
+ }
+ return sDatabase;
+ }
+ }
+
+ /**
+ * Returns the singleton parameter instance for a spherically projected geoid height map and its
+ * corresponding tile management.
+ */
+ @NonNull
+ public static MapParamsProto getParams(@NonNull Context context) throws IOException {
+ synchronized (sLock) {
+ if (sParams == null) {
+ MapParamsEntity current = getDatabase(context).mapParamsDao().getCurrent();
+ if (current == null) {
+ throw new IOException("Unable to load map parameters from raw assets.");
+ }
+ sParams = current.value();
+ }
+ return sParams;
+ }
+ }
+
+ private static long getCacheKey(@NonNull MapParamsProto params, long s2CellId) {
+ return S2CellIdUtils.getParent(s2CellId, params.getCacheTileS2Level());
+ }
+
+ @NonNull
+ private static String getDiskToken(@NonNull MapParamsProto params, long s2CellId) {
+ return S2CellIdUtils.getToken(
+ S2CellIdUtils.getParent(s2CellId, params.getDiskTileS2Level()));
+ }
+
+ /**
+ * Adds to {@code values} values in the unit interval [0, 1] for the map cells identified by
+ * {@code s2CellIds}. Returns true if values are present for all IDs; otherwise, returns false
+ * and adds NaNs for absent values.
+ */
+ private static boolean getUnitIntervalValues(@NonNull MapParamsProto params,
+ @NonNull TileFunction tileFunction,
+ @NonNull long[] s2CellIds, @NonNull double[] values) throws IOException {
+ int len = s2CellIds.length;
+
+ S2TileProto[] tiles = new S2TileProto[len];
+ for (int i = 0; i < len; i++) {
+ long cacheKey = getCacheKey(params, s2CellIds[i]);
+ tiles[i] = tileFunction.getTile(cacheKey);
+ values[i] = Double.NaN;
+ }
+
+ for (int i = 0; i < len; i++) {
+ if (tiles[i] == null || !Double.isNaN(values[i])) {
+ continue;
+ }
+
+ mergeByteBufferValues(params, s2CellIds, tiles, i, values);
+ mergeByteJpegValues(params, s2CellIds, tiles, i, values);
+ mergeBytePngValues(params, s2CellIds, tiles, i, values);
+ }
+
+ boolean allFound = true;
+ for (int i = 0; i < len; i++) {
+ if (Double.isNaN(values[i])) {
+ allFound = false;
+ } else {
+ values[i] = (((int) values[i]) & 0xFF) / 255.0;
+ }
+ }
+ return allFound;
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ private static void mergeByteBufferValues(@NonNull MapParamsProto params,
+ @NonNull long[] s2CellIds,
+ @NonNull S2TileProto[] tiles,
+ int tileIndex, @NonNull double[] values) {
+ ByteString byteString = tiles[tileIndex].getByteBuffer();
+ if (byteString.isEmpty()) {
+ return;
+ }
+
+ ByteBuffer byteBuffer = byteString.asReadOnlyByteBuffer();
+ int tileS2Level =
+ params.getMapS2Level() - Integer.numberOfTrailingZeros(byteBuffer.limit()) / 2;
+ int numBitsLeftOfTile = 2 * tileS2Level + 3;
+
+ for (int i = tileIndex; i < tiles.length; i++) {
+ if (tiles[i] != tiles[tileIndex]) {
+ continue;
+ }
+
+ long maskedS2CellId = s2CellIds[i] & (-1L >>> numBitsLeftOfTile);
+ int numBitsRightOfMap = 2 * (S2CellIdUtils.MAX_LEVEL - params.getMapS2Level()) + 1;
+ int bufferIndex = (int) (maskedS2CellId >>> numBitsRightOfMap);
+ values[i] = Double.isNaN(values[i]) ? 0 : values[i];
+ values[i] += ((int) byteBuffer.get(bufferIndex)) & 0xFF;
+ }
+ }
+
+ private static void mergeByteJpegValues(@NonNull MapParamsProto params,
+ @NonNull long[] s2CellIds,
+ @NonNull S2TileProto[] tiles,
+ int tileIndex, @NonNull double[] values) throws IOException {
+ mergeByteImageValues(params, tiles[tileIndex].getByteJpeg(), s2CellIds, tiles, tileIndex,
+ values);
+ }
+
+ private static void mergeBytePngValues(@NonNull MapParamsProto params,
+ @NonNull long[] s2CellIds,
+ @NonNull S2TileProto[] tiles,
+ int tileIndex, @NonNull double[] values) throws IOException {
+ mergeByteImageValues(params, tiles[tileIndex].getBytePng(), s2CellIds, tiles, tileIndex,
+ values);
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ private static void mergeByteImageValues(@NonNull MapParamsProto params,
+ @NonNull ByteString byteString,
+ @NonNull long[] s2CellIds,
+ @NonNull S2TileProto[] tiles, int tileIndex, @NonNull double[] values)
+ throws IOException {
+ if (byteString.isEmpty()) {
+ return;
+ }
+ Bitmap bitmap;
+ try (InputStream inputStream = byteString.newInput()) {
+ bitmap = BitmapFactory.decodeStream(inputStream);
+ }
+ if (bitmap == null) {
+ return;
+ }
+
+ for (int i = tileIndex; i < tiles.length; i++) {
+ if (tiles[i] != tiles[tileIndex]) {
+ continue;
+ }
+
+ values[i] = Double.isNaN(values[i]) ? 0 : values[i];
+ values[i] += bitmap.getPixel(getIndexX(params, s2CellIds[i], bitmap.getWidth()),
+ getIndexY(params, s2CellIds[i], bitmap.getHeight())) & 0xFF;
+ }
+ }
+
+ /** Returns the X index for an S2 cell within an S2 tile image of specified width. */
+ private static int getIndexX(@NonNull MapParamsProto params, long s2CellId, int width) {
+ return getIndexXOrY(params, S2CellIdUtils.getI(s2CellId), width);
+ }
+
+ /** Returns the Y index for an S2 cell within an S2 tile image of specified height. */
+ private static int getIndexY(@NonNull MapParamsProto params, long s2CellId, int height) {
+ return getIndexXOrY(params, S2CellIdUtils.getJ(s2CellId), height);
+ }
+
+ private static int getIndexXOrY(@NonNull MapParamsProto params, int iOrJ, int widthOrHeight) {
+ return (iOrJ >> (S2CellIdUtils.MAX_LEVEL - params.getMapS2Level())) % widthOrHeight;
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the {@code s2CellIds} has an invalid length or
+ * ID.
+ */
+ private static void validate(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) {
+ Preconditions.checkArgument(s2CellIds.length == 4);
+ for (long s2CellId : s2CellIds) {
+ Preconditions.checkArgument(S2CellIdUtils.getLevel(s2CellId) == params.getMapS2Level());
+ }
+ }
+
+ /**
+ * Returns the geoid heights in meters associated with the map cells identified by
+ * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for
+ * an ID.
+ */
+ @NonNull
+ public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context,
+ @NonNull long[] s2CellIds) throws IOException {
+ validate(params, s2CellIds);
+ double[] heightsMeters = new double[s2CellIds.length];
+ if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
+ return heightsMeters;
+ }
+
+ TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds);
+ if (getGeoidHeights(params, loadedTiles, s2CellIds, heightsMeters)) {
+ return heightsMeters;
+ }
+ throw new IOException("Unable to calculate geoid heights from raw assets.");
+ }
+
+ /**
+ * Same as {@link #readGeoidHeights(MapParamsProto, Context, long[])} except that data will not
+ * be loaded from raw assets. Returns the heights if present for all IDs; otherwise, returns
+ * null.
+ */
+ @Nullable
+ public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull long[] s2CellIds)
+ throws IOException {
+ validate(params, s2CellIds);
+ double[] heightsMeters = new double[s2CellIds.length];
+ if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
+ return heightsMeters;
+ }
+ return null;
+ }
+
+ /**
+ * Adds to {@code heightsMeters} the geoid heights in meters associated with the map cells
+ * identified by {@code s2CellIds}. Returns true if heights are present for all IDs; otherwise,
+ * returns false and adds NaNs for absent heights.
+ */
+ private boolean getGeoidHeights(@NonNull MapParamsProto params,
+ @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds,
+ @NonNull double[] heightsMeters) throws IOException {
+ boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, heightsMeters);
+ for (int i = 0; i < heightsMeters.length; i++) {
+ // NaNs are properly preserved.
+ heightsMeters[i] *= params.getModelAMeters();
+ heightsMeters[i] += params.getModelBMeters();
+ }
+ return allFound;
+ }
+
+ @NonNull
+ private TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params,
+ @NonNull Context context, @NonNull long[] s2CellIds) throws IOException {
+ int len = s2CellIds.length;
+
+ // Enable batch loading by finding all cache keys upfront.
+ long[] cacheKeys = new long[len];
+ for (int i = 0; i < len; i++) {
+ cacheKeys[i] = getCacheKey(params, s2CellIds[i]);
+ }
+
+ // Attempt to load tiles from cache.
+ S2TileProto[] loadedTiles = new S2TileProto[len];
+ String[] diskTokens = new String[len];
+ for (int i = 0; i < len; i++) {
+ if (diskTokens[i] != null) {
+ continue;
+ }
+ loadedTiles[i] = mCacheTiles.get(cacheKeys[i]);
+ diskTokens[i] = getDiskToken(params, cacheKeys[i]);
+
+ // Batch across common cache key.
+ for (int j = i + 1; j < len; j++) {
+ if (cacheKeys[j] == cacheKeys[i]) {
+ loadedTiles[j] = loadedTiles[i];
+ diskTokens[j] = diskTokens[i];
+ }
+ }
+ }
+
+ // Attempt to load tiles from disk.
+ for (int i = 0; i < len; i++) {
+ if (loadedTiles[i] != null) {
+ continue;
+ }
+
+ TilesEntity entity = getDatabase(context).tilesDao().get(diskTokens[i]);
+ if (entity == null) {
+ throw new IOException("Unable to read disk tile of disk token: " + diskTokens[i]);
+ }
+ mergeFromDiskTile(params, entity.tile(), cacheKeys, diskTokens, i, loadedTiles);
+ }
+
+ return cacheKey -> {
+ for (int i = 0; i < cacheKeys.length; i++) {
+ if (cacheKeys[i] == cacheKey) {
+ return loadedTiles[i];
+ }
+ }
+ return null;
+ };
+ }
+
+ private void mergeFromDiskTile(@NonNull MapParamsProto params, @NonNull S2TileProto diskTile,
+ @NonNull long[] cacheKeys, @NonNull String[] diskTokens, int diskTokenIndex,
+ @NonNull S2TileProto[] loadedTiles) throws IOException {
+ int len = cacheKeys.length;
+ int numMapCellsPerCacheTile =
+ 1 << (2 * (params.getMapS2Level() - params.getCacheTileS2Level()));
+
+ // Reusable arrays.
+ long[] s2CellIds = new long[numMapCellsPerCacheTile];
+ double[] values = new double[numMapCellsPerCacheTile];
+ byte[] bytes = new byte[numMapCellsPerCacheTile];
+
+ // Each cache key identifies a different sub-tile of the same disk tile.
+ TileFunction diskTileFunction = cacheKey -> diskTile;
+ for (int i = diskTokenIndex; i < len; i++) {
+ if (!Objects.equals(diskTokens[i], diskTokens[diskTokenIndex])
+ || loadedTiles[i] != null) {
+ continue;
+ }
+
+ // Find all map cells within the current cache tile.
+ long s2CellId = S2CellIdUtils.getTraversalStart(cacheKeys[i], params.getMapS2Level());
+ for (int j = 0; j < numMapCellsPerCacheTile; j++) {
+ s2CellIds[j] = s2CellId;
+ s2CellId = S2CellIdUtils.getTraversalNext(s2CellId);
+ }
+
+ if (!getUnitIntervalValues(params, diskTileFunction, s2CellIds, values)) {
+ throw new IOException("Corrupted disk tile of disk token: " + diskTokens[i]);
+ }
+
+ for (int j = 0; j < numMapCellsPerCacheTile; j++) {
+ bytes[j] = (byte) Math.round(values[j] * 0xFF);
+ }
+ loadedTiles[i] =
+ S2TileProto.newBuilder().setByteBuffer(ByteString.copyFrom(bytes)).build();
+
+ // Batch across common cache key.
+ for (int j = i + 1; j < len; j++) {
+ if (cacheKeys[j] == cacheKeys[i]) {
+ loadedTiles[j] = loadedTiles[i];
+ }
+ }
+
+ // Side load into tile cache.
+ mCacheTiles.put(cacheKeys[i], loadedTiles[i]);
+ }
+ }
+
+ /** Defines a function-like object to retrieve tiles for cache keys. */
+ private interface TileFunction {
+
+ @Nullable
+ S2TileProto getTile(long cacheKey);
+ }
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/S2CellIdUtils.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/S2CellIdUtils.java
new file mode 100644
index 0000000..1c09c33
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/S2CellIdUtils.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * Provides lightweight S2 cell ID utilities without traditional geometry dependencies.
+ *
+ * <p>See <a href="https://s2geometry.io/">the S2 Geometry Library website</a> for more details.
+ */
+final class S2CellIdUtils {
+
+ /** The level of all leaf S2 cells. */
+ static final int MAX_LEVEL = 30;
+
+ private static final int MAX_SIZE = 1 << MAX_LEVEL;
+ private static final double ONE_OVER_MAX_SIZE = 1.0 / MAX_SIZE;
+ private static final int NUM_FACES = 6;
+ private static final int POS_BITS = 2 * MAX_LEVEL + 1;
+ private static final int SWAP_MASK = 0x1;
+ private static final int LOOKUP_BITS = 4;
+ private static final int LOOKUP_MASK = (1 << LOOKUP_BITS) - 1;
+ private static final int INVERT_MASK = 0x2;
+ private static final int LEAF_MASK = 0x1;
+ private static final int[] LOOKUP_POS = new int[1 << (2 * LOOKUP_BITS + 2)];
+ private static final int[] LOOKUP_IJ = new int[1 << (2 * LOOKUP_BITS + 2)];
+ private static final int[] POS_TO_ORIENTATION = {SWAP_MASK, 0, 0, INVERT_MASK + SWAP_MASK};
+ private static final int[][] POS_TO_IJ =
+ {{0, 1, 3, 2}, {0, 2, 3, 1}, {3, 2, 0, 1}, {3, 1, 0, 2}};
+ private static final double UV_LIMIT = calculateUvLimit();
+ private static final UvTransform[] UV_TRANSFORMS = createUvTransforms();
+ private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms();
+
+ // Used to encode (i, j, o) coordinates into primitive longs.
+ private static final int I_SHIFT = 33;
+ private static final int J_SHIFT = 2;
+ private static final long J_MASK = (1L << 31) - 1;
+
+ static {
+ initLookupCells();
+ }
+
+ /** Prevents instantiation. */
+ private S2CellIdUtils() {
+ }
+
+ /**
+ * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
+ * degrees.
+ */
+ static long fromLatLngDegrees(double latDegrees, double lngDegrees) {
+ return fromLatLngRadians(Math.toRadians(latDegrees), Math.toRadians(lngDegrees));
+ }
+
+ /** Returns the leaf S2 cell ID of the specified (face, i, j) coordinate. */
+ public static long fromFij(int face, int i, int j) {
+ int bits = (face & SWAP_MASK);
+ // Update most significant bits.
+ long msb = ((long) face) << (POS_BITS - 33);
+ for (int k = 7; k >= 4; --k) {
+ bits = lookupBits(i, j, k, bits);
+ msb = updateBits(msb, k, bits);
+ bits = maskBits(bits);
+ }
+ // Update least significant bits.
+ long lsb = 0;
+ for (int k = 3; k >= 0; --k) {
+ bits = lookupBits(i, j, k, bits);
+ lsb = updateBits(lsb, k, bits);
+ bits = maskBits(bits);
+ }
+ return (((msb << 32) + lsb) << 1) + 1;
+ }
+
+ /**
+ * Returns the face of the specified S2 cell. The returned face is in [0, 5] for valid S2 cell
+ * IDs. Behavior is undefined for invalid S2 cell IDs.
+ */
+ public static int getFace(long s2CellId) {
+ return (int) (s2CellId >>> POS_BITS);
+ }
+
+ /**
+ * Returns the ID of the parent of the specified S2 cell at the specified parent level.
+ * Behavior is undefined for invalid S2 cell IDs or parent levels not in
+ * [0, {#link #getLevel(s2CellId)}[.
+ */
+ static long getParent(long s2CellId, int level) {
+ long newLsb = getLowestOnBitForLevel(level);
+ return (s2CellId & -newLsb) | newLsb;
+ }
+
+ /**
+ * Inserts into {@code neighbors} the four S2 cell IDs corresponding to the neighboring
+ * cells adjacent across the specified cell's four edges. This array must be of minimum
+ * length four, and elements at the tail end of the array not corresponding to a neighbor
+ * are set to zero. A reference to this array is returned.
+ *
+ * <p>Inserts in the order of down, right, up, and left directions, in that order. All
+ * neighbors are guaranteed to be distinct.
+ */
+ static void getEdgeNeighbors(long s2CellId, @NonNull long[] neighbors) {
+ int level = getLevel(s2CellId);
+ int size = levelToSizeIj(level);
+ int face = getFace(s2CellId);
+ long ijo = toIjo(s2CellId);
+ int i = ijoToI(ijo);
+ int j = ijoToJ(ijo);
+
+ int iPlusSize = i + size;
+ int iMinusSize = i - size;
+ int jPlusSize = j + size;
+ int jMinusSize = j - size;
+ boolean iPlusSizeLtMax = iPlusSize < MAX_SIZE;
+ boolean iMinusSizeGteZero = iMinusSize >= 0;
+ boolean jPlusSizeLtMax = jPlusSize < MAX_SIZE;
+ boolean jMinusSizeGteZero = jMinusSize >= 0;
+
+ int index = 0;
+ // Down direction.
+ neighbors[index++] = getParent(fromFijSame(face, i, jMinusSize, jMinusSizeGteZero),
+ level);
+ // Right direction.
+ neighbors[index++] = getParent(fromFijSame(face, iPlusSize, j, iPlusSizeLtMax), level);
+ // Up direction.
+ neighbors[index++] = getParent(fromFijSame(face, i, jPlusSize, jPlusSizeLtMax), level);
+ // Left direction.
+ neighbors[index++] = getParent(fromFijSame(face, iMinusSize, j, iMinusSizeGteZero),
+ level);
+
+ // Pad end of neighbor array with zeros.
+ Arrays.fill(neighbors, index, neighbors.length, 0);
+ }
+
+ /** Returns the "i" coordinate for the specified S2 cell. */
+ static int getI(long s2CellId) {
+ return ijoToI(toIjo(s2CellId));
+ }
+
+ /** Returns the "j" coordinate for the specified S2 cell. */
+ static int getJ(long s2CellId) {
+ return ijoToJ(toIjo(s2CellId));
+ }
+
+ /**
+ * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
+ * radians.
+ */
+ private static long fromLatLngRadians(double latRadians, double lngRadians) {
+ double cosLat = Math.cos(latRadians);
+ double x = Math.cos(lngRadians) * cosLat;
+ double y = Math.sin(lngRadians) * cosLat;
+ double z = Math.sin(latRadians);
+ return fromXyz(x, y, z);
+ }
+
+ /**
+ * Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid
+ * S2 cell IDs. Behavior is undefined for invalid S2 cell IDs.
+ */
+ static int getLevel(long s2CellId) {
+ if (isLeaf(s2CellId)) {
+ return MAX_LEVEL;
+ }
+ return MAX_LEVEL - (Long.numberOfTrailingZeros(s2CellId) >> 1);
+ }
+
+ /** Returns the lowest-numbered bit that is on for the specified S2 cell. */
+ static long getLowestOnBit(long s2CellId) {
+ return s2CellId & -s2CellId;
+ }
+
+ /** Returns the lowest-numbered bit that is on for any S2 cell on the specified level. */
+ static long getLowestOnBitForLevel(int level) {
+ return 1L << (2 * (MAX_LEVEL - level));
+ }
+
+ /**
+ * Returns the ID of the first S2 cell in a traversal of children map S2 cells, in Hilbert curve
+ * order.
+ */
+ static long getTraversalStart(long s2CellId, int level) {
+ return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level);
+ }
+
+ /** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */
+ static long getTraversalNext(long s2CellId) {
+ return s2CellId + (getLowestOnBit(s2CellId) << 1);
+ }
+
+ /**
+ * Encodes the S2 cell id to compact text strings suitable for display or indexing. Cells at
+ * lower levels (i.e., larger cells) are encoded into fewer characters.
+ */
+ @NonNull
+ static String getToken(long s2CellId) {
+ if (s2CellId == 0) {
+ return "X";
+ }
+
+ // Convert to a hex string with as many digits as necessary.
+ String hex = Long.toHexString(s2CellId).toLowerCase(Locale.US);
+ // Prefix 0s to get a length 16 string.
+ String padded = padStart(hex);
+ // Trim zeroes off the end.
+ return padded.replaceAll("0*$", "");
+ }
+
+ private static String padStart(String string) {
+ if (string.length() >= 16) {
+ return string;
+ }
+ StringBuilder sb = new StringBuilder(16);
+ for (int i = string.length(); i < 16; i++) {
+ sb.append('0');
+ }
+ sb.append(string);
+ return sb.toString();
+ }
+
+ /** Returns the leaf S2 cell ID of the specified (x, y, z) coordinate. */
+ private static long fromXyz(double x, double y, double z) {
+ int face = xyzToFace(x, y, z);
+ UvTransform uvTransform = UV_TRANSFORMS[face];
+ double u = uvTransform.xyzToU(x, y, z);
+ double v = uvTransform.xyzToV(x, y, z);
+ return fromFuv(face, u, v);
+ }
+
+ /** Returns the leaf S2 cell ID of the specified (face, u, v) coordinate. */
+ private static long fromFuv(int face, double u, double v) {
+ int i = uToI(u);
+ int j = vToJ(v);
+ return fromFij(face, i, j);
+ }
+
+ private static long fromFijWrap(int face, int i, int j) {
+ double u = iToU(i);
+ double v = jToV(j);
+
+ XyzTransform xyzTransform = XYZ_TRANSFORMS[face];
+ double x = xyzTransform.uvToX(u, v);
+ double y = xyzTransform.uvToY(u, v);
+ double z = xyzTransform.uvToZ(u, v);
+
+ int newFace = xyzToFace(x, y, z);
+ UvTransform uvTransform = UV_TRANSFORMS[newFace];
+ double newU = uvTransform.xyzToU(x, y, z);
+ double newV = uvTransform.xyzToV(x, y, z);
+
+ int newI = uShiftIntoI(newU);
+ int newJ = vShiftIntoJ(newV);
+ return fromFij(newFace, newI, newJ);
+ }
+
+ private static long fromFijSame(int face, int i, int j, boolean isSameFace) {
+ if (isSameFace) {
+ return fromFij(face, i, j);
+ }
+ return fromFijWrap(face, i, j);
+ }
+
+ /**
+ * Returns the face associated with the specified (x, y, z) coordinate. For a coordinate
+ * on a face boundary, the returned face is arbitrary but repeatable.
+ */
+ private static int xyzToFace(double x, double y, double z) {
+ double absX = Math.abs(x);
+ double absY = Math.abs(y);
+ double absZ = Math.abs(z);
+ if (absX > absY) {
+ if (absX > absZ) {
+ return (x < 0) ? 3 : 0;
+ }
+ return (z < 0) ? 5 : 2;
+ }
+ if (absY > absZ) {
+ return (y < 0) ? 4 : 1;
+ }
+ return (z < 0) ? 5 : 2;
+ }
+
+ private static int uToI(double u) {
+ double s;
+ if (u >= 0) {
+ s = 0.5 * Math.sqrt(1 + 3 * u);
+ } else {
+ s = 1 - 0.5 * Math.sqrt(1 - 3 * u);
+ }
+ return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5)));
+ }
+
+ private static int vToJ(double v) {
+ // Same calculation as uToI.
+ return uToI(v);
+ }
+
+ private static int lookupBits(int i, int j, int k, int bits) {
+ bits += ((i >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << (LOOKUP_BITS + 2);
+ bits += ((j >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << 2;
+ return LOOKUP_POS[bits];
+ }
+
+ private static long updateBits(long sb, int k, int bits) {
+ return sb | ((((long) bits) >> 2) << ((k & 0x3) * 2 * LOOKUP_BITS));
+ }
+
+ private static int maskBits(int bits) {
+ return bits & (SWAP_MASK | INVERT_MASK);
+ }
+
+ private static boolean isLeaf(long s2CellId) {
+ return ((int) s2CellId & LEAF_MASK) != 0;
+ }
+
+ private static double iToU(int i) {
+ int satI = Math.max(-1, Math.min(MAX_SIZE, i));
+ return Math.max(
+ -UV_LIMIT,
+ Math.min(UV_LIMIT, ONE_OVER_MAX_SIZE * ((satI << 1) + 1 - MAX_SIZE)));
+ }
+
+ private static double jToV(int j) {
+ // Same calculation as iToU.
+ return iToU(j);
+ }
+
+ private static long toIjo(long s2CellId) {
+ int face = getFace(s2CellId);
+ int bits = face & SWAP_MASK;
+ int i = 0;
+ int j = 0;
+ for (int k = 7; k >= 0; --k) {
+ int nbits = (k == 7) ? (MAX_LEVEL - 7 * LOOKUP_BITS) : LOOKUP_BITS;
+ bits += ((int) (s2CellId >>> (k * 2 * LOOKUP_BITS + 1)) & ((1 << (2 * nbits))
+ - 1)) << 2;
+ bits = LOOKUP_IJ[bits];
+ i += (bits >> (LOOKUP_BITS + 2)) << (k * LOOKUP_BITS);
+ j += ((bits >> 2) & ((1 << LOOKUP_BITS) - 1)) << (k * LOOKUP_BITS);
+ bits &= (SWAP_MASK | INVERT_MASK);
+ }
+ int orientation =
+ ((getLowestOnBit(s2CellId) & 0x1111111111111110L) != 0) ? (bits ^ SWAP_MASK)
+ : bits;
+ return (((long) i) << I_SHIFT) | (((long) j) << J_SHIFT) | orientation;
+ }
+
+ private static int ijoToI(long ijo) {
+ return (int) (ijo >>> I_SHIFT);
+ }
+
+ private static int ijoToJ(long ijo) {
+ return (int) ((ijo >>> J_SHIFT) & J_MASK);
+ }
+
+ private static int uShiftIntoI(double u) {
+ double s = 0.5 * (u + 1);
+ return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5)));
+ }
+
+ private static int vShiftIntoJ(double v) {
+ // Same calculation as uShiftIntoI.
+ return uShiftIntoI(v);
+ }
+
+ private static int levelToSizeIj(int level) {
+ return 1 << (MAX_LEVEL - level);
+ }
+
+ private static void initLookupCells() {
+ initLookupCell(0, 0, 0, 0, 0, 0);
+ initLookupCell(0, 0, 0, SWAP_MASK, 0, SWAP_MASK);
+ initLookupCell(0, 0, 0, INVERT_MASK, 0, INVERT_MASK);
+ initLookupCell(0, 0, 0, SWAP_MASK | INVERT_MASK, 0, SWAP_MASK | INVERT_MASK);
+ }
+
+ private static void initLookupCell(
+ int level, int i, int j, int origOrientation, int pos, int orientation) {
+ if (level == LOOKUP_BITS) {
+ int ij = (i << LOOKUP_BITS) + j;
+ LOOKUP_POS[(ij << 2) + origOrientation] = (pos << 2) + orientation;
+ LOOKUP_IJ[(pos << 2) + origOrientation] = (ij << 2) + orientation;
+ } else {
+ level++;
+ i <<= 1;
+ j <<= 1;
+ pos <<= 2;
+ for (int subPos = 0; subPos < 4; subPos++) {
+ int ij = POS_TO_IJ[orientation][subPos];
+ int orientationMask = POS_TO_ORIENTATION[subPos];
+ initLookupCell(
+ level,
+ i + (ij >>> 1),
+ j + (ij & 0x1),
+ origOrientation,
+ pos + subPos,
+ orientation ^ orientationMask);
+ }
+ }
+ }
+
+ private static double calculateUvLimit() {
+ double machEps = 1.0;
+ do {
+ machEps /= 2.0f;
+ } while ((1.0 + (machEps / 2.0)) != 1.0);
+ return 1.0 + machEps;
+ }
+
+ @NonNull
+ private static UvTransform[] createUvTransforms() {
+ UvTransform[] uvTransforms = new UvTransform[NUM_FACES];
+ uvTransforms[0] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return y / x;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return z / x;
+ }
+ };
+ uvTransforms[1] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return -x / y;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return z / y;
+ }
+ };
+ uvTransforms[2] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return -x / z;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return -y / z;
+ }
+ };
+ uvTransforms[3] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return z / x;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return y / x;
+ }
+ };
+ uvTransforms[4] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return z / y;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return -x / y;
+ }
+ };
+ uvTransforms[5] =
+ new UvTransform() {
+
+ @Override
+ public double xyzToU(double x, double y, double z) {
+ return -y / z;
+ }
+
+ @Override
+ public double xyzToV(double x, double y, double z) {
+ return -x / z;
+ }
+ };
+ return uvTransforms;
+ }
+
+ @NonNull
+ private static XyzTransform[] createXyzTransforms() {
+ XyzTransform[] xyzTransforms = new XyzTransform[NUM_FACES];
+ xyzTransforms[0] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return 1;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return u;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return v;
+ }
+ };
+ xyzTransforms[1] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return -u;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return 1;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return v;
+ }
+ };
+ xyzTransforms[2] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return -u;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return -v;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return 1;
+ }
+ };
+ xyzTransforms[3] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return -1;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return -v;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return -u;
+ }
+ };
+ xyzTransforms[4] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return v;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return -1;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return -u;
+ }
+ };
+ xyzTransforms[5] =
+ new XyzTransform() {
+
+ @Override
+ public double uvToX(double u, double v) {
+ return v;
+ }
+
+ @Override
+ public double uvToY(double u, double v) {
+ return u;
+ }
+
+ @Override
+ public double uvToZ(double u, double v) {
+ return -1;
+ }
+ };
+ return xyzTransforms;
+ }
+
+ /**
+ * Transform from (x, y, z) coordinates to (u, v) coordinates, indexed by face. For a
+ * (x, y, z) coordinate within a face, each element of the resulting (u, v) coordinate
+ * should lie in the inclusive range [-1, 1], with the face center having a (u, v)
+ * coordinate equal to (0, 0).
+ */
+ private interface UvTransform {
+
+ /**
+ * Returns for the specified (x, y, z) coordinate the corresponding u-coordinate
+ * (which may lie outside the range [-1, 1]).
+ */
+ double xyzToU(double x, double y, double z);
+
+ /**
+ * Returns for the specified (x, y, z) coordinate the corresponding v-coordinate
+ * (which may lie outside the range [-1, 1]).
+ */
+ double xyzToV(double x, double y, double z);
+ }
+
+ /**
+ * Transform from (u, v) coordinates to (x, y, z) coordinates, indexed by face. The
+ * resulting vectors are not necessarily of unit length.
+ */
+ private interface XyzTransform {
+
+ /** Returns for the specified (u, v) coordinate the corresponding x-coordinate. */
+ double uvToX(double u, double v);
+
+ /** Returns for the specified (u, v) coordinate the corresponding y-coordinate. */
+ double uvToY(double u, double v);
+
+ /** Returns for the specified (u, v) coordinate the corresponding z-coordinate. */
+ double uvToZ(double u, double v);
+ }
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/AltitudeConverterDatabase.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/AltitudeConverterDatabase.java
new file mode 100644
index 0000000..b84b467
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/AltitudeConverterDatabase.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl.db;
+
+import androidx.annotation.NonNull;
+import androidx.core.location.altitude.impl.AltitudeConverter;
+import androidx.room.Database;
+import androidx.room.RoomDatabase;
+import androidx.room.TypeConverters;
+
+/** Defines the resource database for {@link AltitudeConverter}. */
+@Database(entities = {MapParamsEntity.class, TilesEntity.class}, version = 1, exportSchema = false)
+@TypeConverters({MapParamsEntity.class, TilesEntity.class})
+public abstract class AltitudeConverterDatabase extends RoomDatabase {
+
+ /** Returns the data access object for the MapParams table. */
+ @NonNull
+ public abstract MapParamsDao mapParamsDao();
+
+ /** Returns the data access object for the Tiles table. */
+ @NonNull
+ public abstract TilesDao tilesDao();
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsDao.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsDao.java
new file mode 100644
index 0000000..c5e18b8
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsDao.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl.db;
+
+import androidx.annotation.Nullable;
+import androidx.room.Dao;
+import androidx.room.Query;
+
+/** Provides data access for entities within the MapParams table. */
+@Dao
+public interface MapParamsDao {
+
+ /** Returns the most current map parameters. */
+ @Nullable
+ @Query("SELECT * FROM MapParams ORDER BY id DESC LIMIT 1")
+ MapParamsEntity getCurrent();
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsEntity.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsEntity.java
new file mode 100644
index 0000000..7b27678
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/MapParamsEntity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl.db;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.location.altitude.impl.proto.InvalidProtocolBufferException;
+import androidx.core.location.altitude.impl.proto.MapParamsProto;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+import androidx.room.TypeConverter;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.AutoValue.CopyAnnotations;
+
+import org.jetbrains.annotations.Contract;
+
+/** Defines the entity type and its converters within the MapParams table. */
+@AutoValue
+@Entity(tableName = "MapParams")
+public abstract class MapParamsEntity {
+
+ private static final String TAG = "MapParamsEntity";
+
+ @NonNull
+ @Contract("_, _ -> new")
+ static MapParamsEntity create(int id, MapParamsProto value) {
+ return new AutoValue_MapParamsEntity(id, value);
+ }
+
+ /** Encodes a {@link MapParamsProto} */
+ @NonNull
+ @TypeConverter
+ public static byte[] fromValue(@NonNull MapParamsProto value) {
+ return value.toByteArray();
+ }
+
+ /** Decodes a {@link MapParamsProto} */
+ @Nullable
+ @TypeConverter
+ public static MapParamsProto toValue(@NonNull byte[] byteArray) {
+ try {
+ return MapParamsProto.parseFrom(byteArray);
+ } catch (InvalidProtocolBufferException e) {
+ Log.e(TAG, "Unable to parse map params.");
+ return null;
+ }
+ }
+
+ @CopyAnnotations
+ @PrimaryKey
+ @ColumnInfo(name = "id")
+ abstract int id();
+
+ /**
+ * Returns parameters for a spherically projected geoid map and corresponding tile management.
+ */
+ @CopyAnnotations
+ @ColumnInfo(name = "value")
+ @NonNull
+ public abstract MapParamsProto value();
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesDao.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesDao.java
new file mode 100644
index 0000000..719d171
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesDao.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl.db;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.Dao;
+import androidx.room.Query;
+
+/** Provides data access for entities within the Tiles table. */
+@Dao
+public interface TilesDao {
+
+ /** Returns the tile associated with the provided token. */
+ @Nullable
+ @Query("SELECT * FROM Tiles WHERE token = :token LIMIT 1")
+ TilesEntity get(@NonNull String token);
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesEntity.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesEntity.java
new file mode 100644
index 0000000..6b3f29f
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/TilesEntity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.location.altitude.impl.db;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.location.altitude.impl.proto.InvalidProtocolBufferException;
+import androidx.core.location.altitude.impl.proto.S2TileProto;
+import androidx.room.ColumnInfo;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+import androidx.room.TypeConverter;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.AutoValue.CopyAnnotations;
+
+/** Defines the entity type and its converters within the Tiles table. */
+@AutoValue
+@Entity(tableName = "Tiles")
+public abstract class TilesEntity {
+
+ private static final String TAG = "MapParamsEntity";
+
+ static TilesEntity create(String token, S2TileProto tile) {
+ return new AutoValue_TilesEntity(token, tile);
+ }
+
+ /** Encodes a {@link S2TileProto}. */
+ @NonNull
+ @TypeConverter
+ public static byte[] fromTile(@NonNull S2TileProto tile) {
+ return tile.toByteArray();
+ }
+
+ /** Decodes a {@link S2TileProto}. */
+ @Nullable
+ @TypeConverter
+ public static S2TileProto toTile(@NonNull byte[] byteArray) {
+ try {
+ return S2TileProto.parseFrom(byteArray);
+ } catch (InvalidProtocolBufferException e) {
+ Log.e(TAG, "Unable to parse tile.");
+ return null;
+ }
+ }
+
+ /** Returns an identifier for a tile within an S2 cell ID to unit interval map. */
+ @CopyAnnotations
+ @PrimaryKey
+ @ColumnInfo(name = "token")
+ @NonNull
+ public abstract String token();
+
+ /** Returns a tile within an S2 cell ID to unit interval map. */
+ @CopyAnnotations
+ @ColumnInfo(name = "tile")
+ @NonNull
+ public abstract S2TileProto tile();
+}
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/package-info.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/package-info.java
new file mode 100644
index 0000000..fa7d778
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/db/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+@RestrictTo(LIBRARY)
+package androidx.core.location.altitude.impl.db;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/package-info.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/package-info.java
new file mode 100644
index 0000000..c61153e
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+@RestrictTo(LIBRARY)
+package androidx.core.location.altitude.impl;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/proto/package-info.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/proto/package-info.java
new file mode 100644
index 0000000..81aad0e
--- /dev/null
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/impl/proto/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+@RestrictTo(LIBRARY)
+package androidx.core.location.altitude.impl.proto;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/package-info.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/package-info.java
index b9ddfaa..cf031ab 100644
--- a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/package-info.java
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/package-info.java
@@ -14,7 +14,5 @@
* limitations under the License.
*/
-/**
- * Provides compatibility APIs concerning location altitudes.
- */
+/** Provides compatibility APIs concerning location altitudes. */
package androidx.core.location.altitude;
diff --git a/core/core-performance/src/test/resources/robolectric.properties b/core/core-performance/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/core/core-performance/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/core/core-telecom/OWNERS b/core/core-telecom/OWNERS
new file mode 100644
index 0000000..7de7eb4
--- /dev/null
+++ b/core/core-telecom/OWNERS
@@ -0,0 +1,9 @@
+# Bug component: 151185
+breadley@google.com
+tgunn@google.com
+xiaotonj@google.com
+chinmayd@google.com
+tjstuart@google.com
+rgreenwalt@google.com
+pmadapurmath@google.com
+grantmenke@google.com
diff --git a/core/core-telecom/api/current.txt b/core/core-telecom/api/current.txt
new file mode 100644
index 0000000..aa73740
--- /dev/null
+++ b/core/core-telecom/api/current.txt
@@ -0,0 +1,98 @@
+// Signature format: 4.0
+package androidx.core.telecom {
+
+ public final class CallAttributesCompat {
+ ctor public CallAttributesCompat(CharSequence displayName, android.net.Uri address, int direction, optional int callType, optional int callCapabilities);
+ method public android.net.Uri getAddress();
+ method public int getCallCapabilities();
+ method public int getCallType();
+ method public int getDirection();
+ method public CharSequence getDisplayName();
+ property public final android.net.Uri address;
+ property public final int callCapabilities;
+ property public final int callType;
+ property public final int direction;
+ property public final CharSequence displayName;
+ field public static final int CALL_TYPE_AUDIO_CALL = 1; // 0x1
+ field public static final int CALL_TYPE_VIDEO_CALL = 2; // 0x2
+ field public static final androidx.core.telecom.CallAttributesCompat.Companion Companion;
+ field public static final int DIRECTION_INCOMING = 1; // 0x1
+ field public static final int DIRECTION_OUTGOING = 2; // 0x2
+ field public static final int SUPPORTS_SET_INACTIVE = 2; // 0x2
+ field public static final int SUPPORTS_STREAM = 4; // 0x4
+ field public static final int SUPPORTS_TRANSFER = 8; // 0x8
+ }
+
+ public static final class CallAttributesCompat.Companion {
+ }
+
+ public interface CallControlCallback {
+ method public suspend Object? onAnswer(int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onDisconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onSetActive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onSetInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public interface CallControlScope {
+ method public suspend Object? answer(int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? disconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> getAvailableEndpoints();
+ method public android.os.ParcelUuid getCallId();
+ method public kotlinx.coroutines.flow.Flow<androidx.core.telecom.CallEndpointCompat> getCurrentCallEndpoint();
+ method public kotlinx.coroutines.flow.Flow<java.lang.Boolean> isMuted();
+ method public suspend Object? requestEndpointChange(androidx.core.telecom.CallEndpointCompat endpoint, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? setActive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public void setCallback(androidx.core.telecom.CallControlCallback callControlCallback);
+ method public suspend Object? setInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> availableEndpoints;
+ property public abstract kotlinx.coroutines.flow.Flow<androidx.core.telecom.CallEndpointCompat> currentCallEndpoint;
+ property public abstract kotlinx.coroutines.flow.Flow<java.lang.Boolean> isMuted;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CallEndpointCompat {
+ ctor public CallEndpointCompat(CharSequence name, int type, android.os.ParcelUuid identifier);
+ method public android.os.ParcelUuid getIdentifier();
+ method public CharSequence getName();
+ method public int getType();
+ property public final android.os.ParcelUuid identifier;
+ property public final CharSequence name;
+ property public final int type;
+ field public static final androidx.core.telecom.CallEndpointCompat.Companion Companion;
+ field public static final int TYPE_BLUETOOTH = 2; // 0x2
+ field public static final int TYPE_EARPIECE = 1; // 0x1
+ field public static final int TYPE_SPEAKER = 4; // 0x4
+ field public static final int TYPE_STREAMING = 5; // 0x5
+ field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+ field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
+ }
+
+ public static final class CallEndpointCompat.Companion {
+ }
+
+ public final class CallException extends java.lang.RuntimeException {
+ ctor public CallException(optional int code, optional String? message);
+ method public int getCode();
+ property public final int code;
+ field public static final androidx.core.telecom.CallException.Companion Companion;
+ field public static final int ERROR_CALLBACKS_CODE = 2; // 0x2
+ field public static final int ERROR_UNKNOWN_CODE = 1; // 0x1
+ }
+
+ public static final class CallException.Companion {
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CallsManager {
+ ctor public CallsManager(android.content.Context context);
+ method @RequiresPermission("android.permission.MANAGE_OWN_CALLS") public suspend Object? addCall(androidx.core.telecom.CallAttributesCompat callAttributes, kotlin.jvm.functions.Function1<? super androidx.core.telecom.CallControlScope,kotlin.Unit> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission("android.permission.MANAGE_OWN_CALLS") public void registerAppWithTelecom(int capabilities);
+ field public static final int CAPABILITY_BASELINE = 1; // 0x1
+ field public static final int CAPABILITY_SUPPORTS_CALL_STREAMING = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 2; // 0x2
+ field public static final androidx.core.telecom.CallsManager.Companion Companion;
+ }
+
+ public static final class CallsManager.Companion {
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/core/core-telecom/api/res-current.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to core/core-telecom/api/res-current.txt
diff --git a/core/core-telecom/api/restricted_current.txt b/core/core-telecom/api/restricted_current.txt
new file mode 100644
index 0000000..8c6ce9f
--- /dev/null
+++ b/core/core-telecom/api/restricted_current.txt
@@ -0,0 +1,116 @@
+// Signature format: 4.0
+package androidx.core.telecom {
+
+ public final class CallAttributesCompat {
+ ctor public CallAttributesCompat(CharSequence displayName, android.net.Uri address, @androidx.core.telecom.CallAttributesCompat.Companion.Direction int direction, optional @androidx.core.telecom.CallAttributesCompat.Companion.CallType int callType, optional @androidx.core.telecom.CallAttributesCompat.Companion.CallCapability int callCapabilities);
+ method public android.net.Uri getAddress();
+ method public int getCallCapabilities();
+ method public int getCallType();
+ method public int getDirection();
+ method public CharSequence getDisplayName();
+ property public final android.net.Uri address;
+ property public final int callCapabilities;
+ property public final int callType;
+ property public final int direction;
+ property public final CharSequence displayName;
+ field public static final int CALL_TYPE_AUDIO_CALL = 1; // 0x1
+ field public static final int CALL_TYPE_VIDEO_CALL = 2; // 0x2
+ field public static final androidx.core.telecom.CallAttributesCompat.Companion Companion;
+ field public static final int DIRECTION_INCOMING = 1; // 0x1
+ field public static final int DIRECTION_OUTGOING = 2; // 0x2
+ field public static final int SUPPORTS_SET_INACTIVE = 2; // 0x2
+ field public static final int SUPPORTS_STREAM = 4; // 0x4
+ field public static final int SUPPORTS_TRANSFER = 8; // 0x8
+ }
+
+ public static final class CallAttributesCompat.Companion {
+ }
+
+ @IntDef(value={androidx.core.telecom.CallAttributesCompat.SUPPORTS_SET_INACTIVE, androidx.core.telecom.CallAttributesCompat.SUPPORTS_STREAM, androidx.core.telecom.CallAttributesCompat.SUPPORTS_TRANSFER}, flag=true) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.VALUE_PARAMETER}) public static @interface CallAttributesCompat.Companion.CallCapability {
+ }
+
+ @IntDef({androidx.core.telecom.CallAttributesCompat.CALL_TYPE_AUDIO_CALL, androidx.core.telecom.CallAttributesCompat.CALL_TYPE_VIDEO_CALL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.PROPERTY}) public static @interface CallAttributesCompat.Companion.CallType {
+ }
+
+ @IntDef({androidx.core.telecom.CallAttributesCompat.DIRECTION_INCOMING, androidx.core.telecom.CallAttributesCompat.DIRECTION_OUTGOING}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.VALUE_PARAMETER}) public static @interface CallAttributesCompat.Companion.Direction {
+ }
+
+ public interface CallControlCallback {
+ method public suspend Object? onAnswer(@androidx.core.telecom.CallAttributesCompat.Companion.CallType int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onDisconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onSetActive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? onSetInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public interface CallControlScope {
+ method public suspend Object? answer(@androidx.core.telecom.CallAttributesCompat.Companion.CallType int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? disconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> getAvailableEndpoints();
+ method public android.os.ParcelUuid getCallId();
+ method public kotlinx.coroutines.flow.Flow<androidx.core.telecom.CallEndpointCompat> getCurrentCallEndpoint();
+ method public kotlinx.coroutines.flow.Flow<java.lang.Boolean> isMuted();
+ method public suspend Object? requestEndpointChange(androidx.core.telecom.CallEndpointCompat endpoint, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? setActive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public void setCallback(androidx.core.telecom.CallControlCallback callControlCallback);
+ method public suspend Object? setInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> availableEndpoints;
+ property public abstract kotlinx.coroutines.flow.Flow<androidx.core.telecom.CallEndpointCompat> currentCallEndpoint;
+ property public abstract kotlinx.coroutines.flow.Flow<java.lang.Boolean> isMuted;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CallEndpointCompat {
+ ctor public CallEndpointCompat(CharSequence name, int type, android.os.ParcelUuid identifier);
+ method public android.os.ParcelUuid getIdentifier();
+ method public CharSequence getName();
+ method public int getType();
+ property public final android.os.ParcelUuid identifier;
+ property public final CharSequence name;
+ property public final int type;
+ field public static final androidx.core.telecom.CallEndpointCompat.Companion Companion;
+ field public static final int TYPE_BLUETOOTH = 2; // 0x2
+ field public static final int TYPE_EARPIECE = 1; // 0x1
+ field public static final int TYPE_SPEAKER = 4; // 0x4
+ field public static final int TYPE_STREAMING = 5; // 0x5
+ field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+ field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
+ }
+
+ public static final class CallEndpointCompat.Companion {
+ }
+
+ @IntDef({androidx.core.telecom.CallEndpointCompat.TYPE_UNKNOWN, androidx.core.telecom.CallEndpointCompat.TYPE_EARPIECE, androidx.core.telecom.CallEndpointCompat.TYPE_BLUETOOTH, androidx.core.telecom.CallEndpointCompat.TYPE_WIRED_HEADSET, androidx.core.telecom.CallEndpointCompat.TYPE_SPEAKER, androidx.core.telecom.CallEndpointCompat.TYPE_STREAMING}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.VALUE_PARAMETER}) public static @interface CallEndpointCompat.Companion.EndpointType {
+ }
+
+ public final class CallException extends java.lang.RuntimeException {
+ ctor public CallException(optional @androidx.core.telecom.CallException.Companion.CallErrorCode int code, optional String? message);
+ method public int getCode();
+ property public final int code;
+ field public static final androidx.core.telecom.CallException.Companion Companion;
+ field public static final int ERROR_CALLBACKS_CODE = 2; // 0x2
+ field public static final int ERROR_UNKNOWN_CODE = 1; // 0x1
+ }
+
+ public static final class CallException.Companion {
+ }
+
+ @IntDef({androidx.core.telecom.CallException.ERROR_UNKNOWN_CODE, androidx.core.telecom.CallException.ERROR_CALLBACKS_CODE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) public static @interface CallException.Companion.CallErrorCode {
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CallsManager {
+ ctor public CallsManager(android.content.Context context);
+ method @RequiresPermission("android.permission.MANAGE_OWN_CALLS") public suspend Object? addCall(androidx.core.telecom.CallAttributesCompat callAttributes, kotlin.jvm.functions.Function1<? super androidx.core.telecom.CallControlScope,kotlin.Unit> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission("android.permission.MANAGE_OWN_CALLS") public void registerAppWithTelecom(@androidx.core.telecom.CallsManager.Companion.Capability int capabilities);
+ field public static final int CAPABILITY_BASELINE = 1; // 0x1
+ field public static final int CAPABILITY_SUPPORTS_CALL_STREAMING = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 2; // 0x2
+ field public static final androidx.core.telecom.CallsManager.Companion Companion;
+ }
+
+ public static final class CallsManager.Companion {
+ }
+
+ @IntDef(value={androidx.core.telecom.CallsManager.CAPABILITY_BASELINE, androidx.core.telecom.CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING, androidx.core.telecom.CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING}, flag=true) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE}) public static @interface CallsManager.Companion.Capability {
+ }
+
+}
+
diff --git a/core/core-telecom/build.gradle b/core/core-telecom/build.gradle
new file mode 100644
index 0000000..2e308881
--- /dev/null
+++ b/core/core-telecom/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ // core-telecom dependencies
+ api(libs.kotlinStdlib)
+ api(libs.guavaListenableFuture)
+ implementation("androidx.annotation:annotation:1.4.0")
+ implementation("androidx.core:core:1.9.0")
+ implementation(libs.kotlinCoroutinesCore)
+ implementation(libs.kotlinCoroutinesGuava)
+ // Test dependencies
+ androidTestImplementation(project(":internal-testutils-common"))
+ androidTestImplementation(libs.kotlinStdlib)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.truth)
+ androidTestImplementation(libs.espressoCore)
+ androidTestImplementation(libs.multidex)
+}
+
+android {
+ namespace "androidx.core.telecom"
+}
+
+androidx {
+ name = "androidx.core:core-telecom"
+ type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.CORE_TELECOM
+ inceptionYear = "2023"
+ description = "Integrate VoIP calls with the Telecom framework."
+}
diff --git a/core/core-telecom/integration-tests/testapp/build.gradle b/core/core-telecom/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..582f731
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("kotlin-android")
+}
+
+android {
+ namespace 'androidx.core.telecom.test'
+
+ defaultConfig {
+ applicationId "androidx.core.telecom.test"
+ minSdk 21
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+ implementation(libs.constraintLayout)
+ implementation("androidx.annotation:annotation:1.4.0")
+ implementation("androidx.core:core:1.9.0")
+ implementation(project(":core:core-telecom"))
+ implementation('androidx.appcompat:appcompat:1.6.1')
+ implementation('androidx.navigation:navigation-fragment-ktx:2.5.3')
+ implementation('androidx.navigation:navigation-ui-ktx:2.5.3')
+ implementation('androidx.recyclerview:recyclerview:1.2.1')
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testRunner)
+}
+
diff --git a/core/core-telecom/integration-tests/testapp/src/main/AndroidManifest.xml b/core/core-telecom/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..529aa31
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat">
+ <activity
+ android:name=".CallingMainActivity"
+ android:exported="true"
+ android:label="@string/main_activity_name"
+ android:theme="@style/Theme.AppCompat">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallListAdapter.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallListAdapter.kt
new file mode 100644
index 0000000..7c33fc8
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallListAdapter.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.test
+
+import android.telecom.CallEndpoint
+import android.telecom.DisconnectCause
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import androidx.annotation.RequiresApi
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@RequiresApi(34)
+class CallListAdapter(private var mList: ArrayList<CallRow>?) :
+ RecyclerView.Adapter<CallListAdapter.ViewHolder>() {
+
+ var mCallIdToViewHolder: MutableMap<String, ViewHolder> = mutableMapOf()
+
+ class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+ // TextViews
+ val callCount: TextView = itemView.findViewById(R.id.callNumber)
+ val callIdTextView: TextView = itemView.findViewById(R.id.callIdTextView)
+ val currentState: TextView = itemView.findViewById(R.id.callStateTextView)
+ val currentEndpoint: TextView = itemView.findViewById(R.id.endpointStateTextView)
+
+ // Call State Buttons
+ val activeButton: Button = itemView.findViewById(R.id.activeButton)
+ val holdButton: Button = itemView.findViewById(R.id.holdButton)
+ val disconnectButton: Button = itemView.findViewById(R.id.disconnectButton)
+
+ // Call Audio Buttons
+ val earpieceButton: Button = itemView.findViewById(R.id.earpieceButton)
+ val speakerButton: Button = itemView.findViewById(R.id.speakerButton)
+ val bluetoothButton: Button = itemView.findViewById(R.id.bluetoothButton)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ // inflates the card_view_design view that is used to hold list item
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.call_row, parent, false)
+
+ return ViewHolder(view)
+ }
+
+ override fun getItemCount(): Int {
+ return mList?.size ?: 0
+ }
+
+ // Set the data for the user
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val ItemsViewModel = mList?.get(position)
+
+ // sets the text to the textview from our itemHolder class
+ if (ItemsViewModel != null) {
+ mCallIdToViewHolder[ItemsViewModel.callObject.mTelecomCallId] = holder
+
+ holder.callCount.text = "Call # " + ItemsViewModel.callNumber.toString() + "; "
+ holder.callIdTextView.text = "ID=[" + ItemsViewModel.callObject.mTelecomCallId + "]"
+
+ holder.activeButton.setOnClickListener {
+ CoroutineScope(Dispatchers.Main).launch {
+ if (ItemsViewModel.callObject.mCallControl!!.setActive()) {
+ holder.currentState.text = "CurrentState=[active]"
+ }
+ }
+ }
+
+ holder.holdButton.setOnClickListener {
+ CoroutineScope(Dispatchers.Main).launch {
+ if (ItemsViewModel.callObject.mCallControl!!.setInactive()) {
+ holder.currentState.text = "CurrentState=[onHold]"
+ }
+ }
+ }
+
+ holder.disconnectButton.setOnClickListener {
+ CoroutineScope(Dispatchers.IO).launch {
+ ItemsViewModel.callObject.mCallControl?.disconnect(
+ DisconnectCause(
+ DisconnectCause.LOCAL
+ )
+ )
+ }
+ holder.currentState.text = "CurrentState=[null]"
+ mList?.remove(ItemsViewModel)
+ this.notifyDataSetChanged()
+ }
+
+ holder.earpieceButton.setOnClickListener {
+ CoroutineScope(Dispatchers.Main).launch {
+ val earpieceEndpoint =
+ ItemsViewModel.callObject.getEndpointType(CallEndpoint.TYPE_EARPIECE)
+ if (earpieceEndpoint != null) {
+ ItemsViewModel.callObject.mCallControl?.requestEndpointChange(
+ earpieceEndpoint
+ )
+ }
+ }
+ }
+ holder.speakerButton.setOnClickListener {
+ CoroutineScope(Dispatchers.Main).launch {
+ val speakerEndpoint = ItemsViewModel.callObject
+ .getEndpointType(CallEndpoint.TYPE_SPEAKER)
+ if (speakerEndpoint != null) {
+ val success = ItemsViewModel.callObject.mCallControl?.requestEndpointChange(
+ speakerEndpoint
+ )
+ if (success == true) {
+ holder.currentEndpoint.text = "currentEndpoint=[speaker]"
+ }
+ }
+ }
+ }
+
+ holder.bluetoothButton.setOnClickListener {
+ CoroutineScope(Dispatchers.Main).launch {
+ val bluetoothEndpoint = ItemsViewModel.callObject
+ .getEndpointType(CallEndpoint.TYPE_BLUETOOTH)
+ if (bluetoothEndpoint != null) {
+ val success = ItemsViewModel.callObject.mCallControl?.requestEndpointChange(
+ bluetoothEndpoint
+ )
+ if (success == true) {
+ holder.currentEndpoint.text =
+ "currentEndpoint=[BT:${bluetoothEndpoint.name}]"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fun updateCallState(callId: String, state: String) {
+ CoroutineScope(Dispatchers.Main).launch {
+ val holder = mCallIdToViewHolder[callId]
+ holder?.callIdTextView?.text = "currentState=[$state]"
+ }
+ }
+
+ fun updateEndpoint(callId: String, endpoint: String) {
+ CoroutineScope(Dispatchers.Main).launch {
+ val holder = mCallIdToViewHolder[callId]
+ holder?.currentEndpoint?.text = "currentEndpoint=[$endpoint]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallRow.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallRow.kt
new file mode 100644
index 0000000..484a17e
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallRow.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.test
+
+data class CallRow(val callNumber: Int, val callObject: VoipCall)
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt
new file mode 100644
index 0000000..f1cebe4
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.test
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.os.Bundle
+import android.telecom.DisconnectCause
+import android.util.Log
+import android.widget.Button
+import android.widget.CheckBox
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallAttributesCompat
+import androidx.core.telecom.CallsManager
+import androidx.core.view.WindowCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@RequiresApi(34)
+class CallingMainActivity : Activity() {
+ // Activity
+ private val TAG = CallingMainActivity::class.simpleName
+ private val mScope = CoroutineScope(Dispatchers.Default)
+ private var mCallCount: Int = 0
+
+ // Telecom
+ private var mCallsManager: CallsManager? = null
+
+ // Call Log objects
+ private var mRecyclerView: RecyclerView? = null
+ private var mCallObjects: ArrayList<CallRow> = ArrayList()
+ private var mAdapter: CallListAdapter? = CallListAdapter(mCallObjects)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+
+ mCallsManager = CallsManager(this)
+ mCallCount = 0
+
+ val registerPhoneAccountButton = findViewById<Button>(R.id.registerButton)
+ registerPhoneAccountButton.setOnClickListener {
+ mScope.launch {
+ registerPhoneAccount()
+ }
+ }
+
+ val addOutgoingCallButton = findViewById<Button>(R.id.addOutgoingCall)
+ addOutgoingCallButton.setOnClickListener {
+ mScope.launch {
+ addCallWithAttributes(Utilities.OUTGOING_CALL_ATTRIBUTES)
+ }
+ }
+
+ val addIncomingCallButton = findViewById<Button>(R.id.addIncomingCall)
+ addIncomingCallButton.setOnClickListener {
+ mScope.launch {
+ addCallWithAttributes(Utilities.INCOMING_CALL_ATTRIBUTES)
+ }
+ }
+
+ // set up the call list view holder
+ mRecyclerView = findViewById(R.id.callListRecyclerView)
+ mRecyclerView?.layoutManager = LinearLayoutManager(this)
+ mRecyclerView?.adapter = mAdapter
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ for (call in mCallObjects) {
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ call.callObject.mCallControl?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
+ } catch (e: Exception) {
+ Log.i(TAG, "onDestroy: exception hit trying to destroy")
+ }
+ }
+ }
+ }
+
+ @SuppressLint("WrongConstant")
+ private fun registerPhoneAccount() {
+ var capabilities: @CallsManager.Companion.Capability Int = CallsManager.CAPABILITY_BASELINE
+
+ val videoCallingCheckBox = findViewById<CheckBox>(R.id.VideoCallingCheckBox)
+ if (videoCallingCheckBox.isChecked) {
+ capabilities = capabilities or CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING
+ }
+ val streamingCheckBox = findViewById<CheckBox>(R.id.streamingCheckBox)
+ if (streamingCheckBox.isChecked) {
+ capabilities = capabilities or CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING
+ }
+ mCallsManager?.registerAppWithTelecom(capabilities)
+ }
+
+ private suspend fun addCallWithAttributes(attributes: CallAttributesCompat) {
+ Log.i(TAG, "addCallWithAttributes: attributes=$attributes")
+ val callObject = VoipCall()
+
+ CoroutineScope(Dispatchers.IO).launch {
+ val coroutineScope = this
+ try {
+ mCallsManager!!.addCall(attributes) {
+ // set the client callback implementation
+ setCallback(callObject.mCallControlCallbackImpl)
+
+ // inject client control interface into the VoIP call object
+ callObject.setCallId(getCallId().toString())
+ callObject.setCallControl(this)
+
+ // Collect updates
+ currentCallEndpoint
+ .onEach { callObject.onCallEndpointChanged(it) }
+ .launchIn(coroutineScope)
+
+ availableEndpoints
+ .onEach { callObject.onAvailableCallEndpointsChanged(it) }
+ .launchIn(coroutineScope)
+
+ isMuted
+ .onEach { callObject.onMuteStateChanged(it) }
+ .launchIn(coroutineScope)
+ }
+ addCallRow(callObject)
+ } catch (e: CancellationException) {
+ Log.i(TAG, "addCallWithAttributes: cancellationException:$e")
+ }
+ }
+ }
+
+ private fun addCallRow(callObject: VoipCall) {
+ mCallObjects.add(CallRow(++mCallCount, callObject))
+ callObject.setCallAdapter(mAdapter)
+ updateCallList()
+ }
+
+ private fun updateCallList() {
+ runOnUiThread {
+ mAdapter?.notifyDataSetChanged()
+ }
+ }
+}
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/Utilities.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/Utilities.kt
new file mode 100644
index 0000000..13c0b67
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/Utilities.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.test
+
+import android.net.Uri
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallAttributesCompat
+import androidx.core.telecom.CallAttributesCompat.Companion.CALL_TYPE_VIDEO_CALL
+import androidx.core.telecom.CallAttributesCompat.Companion.DIRECTION_INCOMING
+import androidx.core.telecom.CallAttributesCompat.Companion.DIRECTION_OUTGOING
+
+@RequiresApi(34)
+class Utilities {
+ companion object {
+ const val APP_SCHEME = "MyCustomScheme"
+ const val ALL_CALL_CAPABILITIES = (CallAttributesCompat.SUPPORTS_SET_INACTIVE
+ or CallAttributesCompat.SUPPORTS_STREAM or CallAttributesCompat.SUPPORTS_TRANSFER)
+
+ // outgoing attributes constants
+ const val OUTGOING_NAME = "Darth Maul"
+ val OUTGOING_URI: Uri = Uri.parse("tel:6506958985")
+ // Define the minimal set of properties to start an outgoing call
+ var OUTGOING_CALL_ATTRIBUTES = CallAttributesCompat(
+ OUTGOING_NAME,
+ OUTGOING_URI,
+ DIRECTION_OUTGOING)
+
+ // incoming attributes constants
+ const val INCOMING_NAME = "Sundar Pichai"
+ val INCOMING_URI: Uri = Uri.parse("tel:6506958985")
+ // Define all possible properties for CallAttributes
+ val INCOMING_CALL_ATTRIBUTES =
+ CallAttributesCompat(
+ INCOMING_NAME,
+ INCOMING_URI,
+ DIRECTION_INCOMING,
+ CALL_TYPE_VIDEO_CALL,
+ ALL_CALL_CAPABILITIES)
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/VoipCall.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/VoipCall.kt
new file mode 100644
index 0000000..d2decbb
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/VoipCall.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.test
+
+import android.telecom.DisconnectCause
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallControlCallback
+import androidx.core.telecom.CallControlScope
+import androidx.core.telecom.CallEndpointCompat
+
+@RequiresApi(34)
+class VoipCall {
+ private val TAG = VoipCall::class.simpleName
+
+ var mAdapter: CallListAdapter? = null
+ var mCallControl: CallControlScope? = null
+ var mCurrentEndpoint: CallEndpointCompat? = null
+ var mAvailableEndpoints: List<CallEndpointCompat>? = ArrayList()
+ var mIsMuted = false
+ var mTelecomCallId: String = ""
+
+ val mCallControlCallbackImpl = object : CallControlCallback {
+ override suspend fun onSetActive(): Boolean {
+ mAdapter?.updateCallState(mTelecomCallId, "Active")
+ return true
+ }
+ override suspend fun onSetInactive(): Boolean {
+ mAdapter?.updateCallState(mTelecomCallId, "Inactive")
+ return true
+ }
+ override suspend fun onAnswer(callType: Int): Boolean {
+ mAdapter?.updateCallState(mTelecomCallId, "Answered")
+ return true
+ }
+ override suspend fun onDisconnect(disconnectCause: DisconnectCause): Boolean {
+ mAdapter?.updateCallState(mTelecomCallId, "Disconnected")
+ return true
+ }
+ }
+
+ fun setCallControl(callControl: CallControlScope) {
+ mCallControl = callControl
+ }
+
+ fun setCallAdapter(adapter: CallListAdapter?) {
+ mAdapter = adapter
+ }
+
+ fun setCallId(callId: String) {
+ mTelecomCallId = callId
+ }
+
+ fun onCallEndpointChanged(endpoint: CallEndpointCompat) {
+ Log.i(TAG, "onCallEndpointChanged: endpoint=$endpoint")
+ mCurrentEndpoint = endpoint
+ mAdapter?.updateEndpoint(mTelecomCallId, endpoint.name.toString())
+ }
+
+ fun onAvailableCallEndpointsChanged(endpoints: List<CallEndpointCompat>) {
+ Log.i(TAG, "onAvailableCallEndpointsChanged:")
+ for (endpoint in endpoints) {
+ Log.i(TAG, "onAvailableCallEndpointsChanged: --> endpoint=$endpoint")
+ }
+ mAvailableEndpoints = endpoints
+ }
+
+ fun onMuteStateChanged(isMuted: Boolean) {
+ Log.i(TAG, "onMuteStateChanged: isMuted=$isMuted")
+ mIsMuted = isMuted
+ }
+
+ fun getEndpointType(type: Int): CallEndpointCompat? {
+ for (endpoint in mAvailableEndpoints!!) {
+ if (endpoint.type == type) {
+ return endpoint
+ }
+ }
+ return null
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/drawable/android.xml b/core/core-telecom/integration-tests/testapp/src/main/res/drawable/android.xml
new file mode 100644
index 0000000..dfa932e
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/drawable/android.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="160dp"
+ android:height="160dp"
+ android:viewportHeight="432"
+ android:viewportWidth="432">
+
+ <!-- Safe zone = 66dp => 432 * (66 / 108) = 432 * 0.61 -->
+ <group
+ android:translateX="84"
+ android:translateY="84"
+ android:scaleX="0.61"
+ android:scaleY="0.61">
+
+ <path
+ android:fillColor="#3ddc84"
+ android:pathData="m322.02,167.89c12.141,-21.437 25.117,-42.497 36.765,-64.158 2.2993,-7.7566 -9.5332,-12.802 -13.555,-5.7796 -12.206,21.045 -24.375,42.112 -36.567,63.166 -57.901,-26.337 -127.00,-26.337 -184.90,0.0 -12.685,-21.446 -24.606,-43.441 -37.743,-64.562 -5.6074,-5.8390 -15.861,1.9202 -11.747,8.8889 12.030,20.823 24.092,41.629 36.134,62.446C47.866,200.90 5.0987,267.15 0.0,337.5c144.00,0.0 288.00,0.0 432.0,0.0C426.74,267.06 384.46,201.32 322.02,167.89ZM116.66,276.03c-13.076,0.58968 -22.531,-15.277 -15.773,-26.469 5.7191,-11.755 24.196,-12.482 30.824,-1.2128 7.8705,11.451 -1.1102,28.027 -15.051,27.682zM315.55,276.03c-13.076,0.58968 -22.531,-15.277 -15.773,-26.469 5.7191,-11.755 24.196,-12.482 30.824,-1.2128 7.8705,11.451 -1.1097,28.027 -15.051,27.682z"
+ android:strokeWidth="2" />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/drawable/ic_launcher.xml b/core/core-telecom/integration-tests/testapp/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000..481bbd7
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/android" />
+</layer-list>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/layout/activity_main.xml b/core/core-telecom/integration-tests/testapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ae035a5
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:context=".CallingMainActivity">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/app_name"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+
+ <CheckBox
+ android:id="@+id/VideoCallingCheckBox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="CAPABILITY_SUPPORTS_VIDEO_CALLING" />
+
+ <CheckBox
+ android:id="@+id/streamingCheckBox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="CAPABILITY_SUPPORTS_CALL_STREAMING" />
+
+ <Button
+ android:id="@+id/registerButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/register_button_text"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/add_out_call_button_text"
+ android:id="@+id/addOutgoingCall"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/add_in_call_button_text"
+ android:id="@+id/addIncomingCall"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/callListRecyclerView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:itemCount="3" />
+
+ </LinearLayout>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/layout/call_row.xml b/core/core-telecom/integration-tests/testapp/src/main/res/layout/call_row.xml
new file mode 100644
index 0000000..9001096
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/layout/call_row.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/callNumber"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="call # -" />
+
+ <TextView
+ android:id="@+id/callIdTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="callId" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/callStateTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="currentCallState=[null]; " />
+
+ <TextView
+ android:id="@+id/endpointStateTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="currentEndpoint=[null]" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/activeButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Active" />
+
+ <Button
+ android:id="@+id/holdButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Hold" />
+
+ <Button
+ android:id="@+id/disconnectButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Disc." />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/earpieceButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="earpiece" />
+
+ <Button
+ android:id="@+id/speakerButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="speaker" />
+
+ <Button
+ android:id="@+id/bluetoothButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="bluetooth" />
+ </LinearLayout>
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values-land/dimens.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values-land/dimens.xml
new file mode 100644
index 0000000..6a160a9
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values-land/dimens.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="fab_margin">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values-w1240dp/dimens.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values-w1240dp/dimens.xml
new file mode 100644
index 0000000..ba6cad4
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values-w1240dp/dimens.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="fab_margin">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values-w600dp/dimens.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values-w600dp/dimens.xml
new file mode 100644
index 0000000..6a160a9
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values-w600dp/dimens.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="fab_margin">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values/colors.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d70ea01
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values/dimens.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..fc04383
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values/strings.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8d10c8c
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="app_name">Telecom Jetpack Test App</string>
+ <string name="main_activity_name">Tel-Jetpack Activity</string>
+ <string name="register_button_text">Register App Phone Account</string>
+ <string name="add_out_call_button_text">+ Outgoing Call </string>
+ <string name="add_in_call_button_text">+ Incoming Call </string>
+
+ <string name="action_settings">Settings</string>
+ <!-- Strings used for fragments for navigation -->
+ <string name="first_fragment_label">First Fragment</string>
+ <string name="second_fragment_label">Second Fragment</string>
+ <string name="next">Next</string>
+ <string name="previous">Previous</string>
+
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/integration-tests/testapp/src/main/res/values/themes.xml b/core/core-telecom/integration-tests/testapp/src/main/res/values/themes.xml
new file mode 100644
index 0000000..14dbff3
--- /dev/null
+++ b/core/core-telecom/integration-tests/testapp/src/main/res/values/themes.xml
@@ -0,0 +1,21 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="Theme.Androidx.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+ <style name="Theme.Androidx.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+ <style name="AppTheme" parent="ThemeOverlay.AppCompat.Light" />
+</resources>
\ No newline at end of file
diff --git a/core/core-telecom/lint-baseline.xml b/core/core-telecom/lint-baseline.xml
new file mode 100644
index 0000000..0df9fc9
--- /dev/null
+++ b/core/core-telecom/lint-baseline.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.1.0-alpha07">
+ <issue
+ id="ImplicitCastClassVerificationFailure"
+ message="This expression has type android.telecom.CallException (introduced in API level 34) but it used as type java.lang.Throwable (introduced in API level 1). Run-time class verification will not be able to validate this implicit cast on devices between these API levels."
+ errorLine1=" openResult.completeExceptionally(reason)"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/core/telecom/CallsManager.kt"/>
+ </issue>
+</issues>
diff --git a/core/core-telecom/src/androidTest/AndroidManifest.xml b/core/core-telecom/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..879f6d8
--- /dev/null
+++ b/core/core-telecom/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+
+ <application>
+ <service
+ android:name="androidx.core.telecom.internal.JetpackConnectionService"
+ android:exported="true"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/BaseTelecomTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/BaseTelecomTest.kt
new file mode 100644
index 0000000..a044deb
--- /dev/null
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/BaseTelecomTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.content.Context
+import android.os.Build
+import android.telecom.PhoneAccountHandle
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.internal.JetpackConnectionService
+import androidx.core.telecom.internal.utils.Utils
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+import androidx.testutils.TestExecutor
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.asCoroutineDispatcher
+import org.junit.After
+import org.junit.Before
+
+@RequiresApi(Build.VERSION_CODES.O)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O /* api=26 */)
+abstract class BaseTelecomTest {
+ val mContext: Context = ApplicationProvider.getApplicationContext()
+ val mWorkerExecutor = TestExecutor()
+ val mWorkerContext: CoroutineContext = mWorkerExecutor.asCoroutineDispatcher()
+
+ lateinit var mCallsManager: CallsManager
+ lateinit var mPackagePhoneAccountHandle: PhoneAccountHandle
+ internal lateinit var mConnectionService: JetpackConnectionService
+
+ @Before
+ fun setUpBase() {
+ Utils.resetUtils()
+ mCallsManager = CallsManager(mContext)
+ mConnectionService = mCallsManager.mConnectionService
+ mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
+ mPackagePhoneAccountHandle = mCallsManager.getPhoneAccountHandleForPackage()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @After
+ fun onDestroyBase() {
+ Utils.resetUtils()
+ JetpackConnectionService.mPendingConnectionRequests.clear()
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallEndpointCompatTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallEndpointCompatTest.kt
new file mode 100644
index 0000000..1711968
--- /dev/null
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallEndpointCompatTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.os.Build.VERSION_CODES
+import android.os.ParcelUuid
+import android.telecom.CallAudioState
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.internal.utils.EndpointUtils
+import androidx.test.filters.SdkSuppress
+import java.util.UUID
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@RequiresApi(VERSION_CODES.O)
+class CallEndpointCompatTest {
+
+ @Test
+ fun testCallEndpointConstructor() {
+ val name = "Endpoint"
+ val type = CallEndpointCompat.TYPE_EARPIECE
+ val identifier = ParcelUuid.fromString(UUID.randomUUID().toString())
+ val endpoint = CallEndpointCompat(name, type, identifier)
+ assertEquals(name, endpoint.name)
+ assertEquals(type, endpoint.type)
+ assertEquals(identifier, endpoint.identifier)
+ }
+
+ @SdkSuppress(minSdkVersion = VERSION_CODES.O)
+ @Test
+ fun testWrappingAudioStateIntoAEndpoint() {
+ val state = CallAudioState(false, CallAudioState.ROUTE_EARPIECE, 0)
+ val endpoint = EndpointUtils.toCallEndpointCompat(state)
+ assertEquals("EARPIECE", endpoint.name)
+ assertEquals(CallEndpointCompat.TYPE_EARPIECE, endpoint.type)
+ }
+
+ @SdkSuppress(minSdkVersion = VERSION_CODES.O)
+ @Test
+ fun testSupportedMask() {
+ val supportedRouteMask = CallAudioState.ROUTE_EARPIECE or
+ CallAudioState.ROUTE_SPEAKER or CallAudioState.ROUTE_WIRED_HEADSET
+ val state = CallAudioState(false, CallAudioState.ROUTE_EARPIECE, supportedRouteMask)
+ val endpoints = EndpointUtils.toCallEndpointsCompat(state)
+ assertEquals(3, endpoints.size)
+ }
+
+ @SdkSuppress(minSdkVersion = VERSION_CODES.O)
+ @Test
+ fun testCallAudioRouteToEndpointTypeMapping() {
+ assertEquals(
+ CallEndpointCompat.TYPE_EARPIECE,
+ EndpointUtils.mapRouteToType(CallAudioState.ROUTE_EARPIECE)
+ )
+ assertEquals(
+ CallEndpointCompat.TYPE_SPEAKER,
+ EndpointUtils.mapRouteToType(CallAudioState.ROUTE_SPEAKER)
+ )
+ assertEquals(
+ CallEndpointCompat.TYPE_WIRED_HEADSET,
+ EndpointUtils.mapRouteToType(CallAudioState.ROUTE_WIRED_HEADSET)
+ )
+ assertEquals(
+ CallEndpointCompat.TYPE_BLUETOOTH,
+ EndpointUtils.mapRouteToType(CallAudioState.ROUTE_BLUETOOTH)
+ )
+ assertEquals(
+ CallEndpointCompat.TYPE_STREAMING,
+ EndpointUtils.mapRouteToType(CallAudioState.ROUTE_STREAMING)
+ )
+ assertEquals(CallEndpointCompat.TYPE_UNKNOWN, EndpointUtils.mapRouteToType(-1))
+ }
+
+ @SdkSuppress(minSdkVersion = VERSION_CODES.O)
+ @Test
+ fun testTypeToRouteMapping() {
+ assertEquals(
+ CallAudioState.ROUTE_EARPIECE,
+ EndpointUtils.mapTypeToRoute(CallEndpointCompat.TYPE_EARPIECE)
+ )
+ assertEquals(
+ CallAudioState.ROUTE_SPEAKER,
+ EndpointUtils.mapTypeToRoute(CallEndpointCompat.TYPE_SPEAKER)
+ )
+ assertEquals(
+ CallAudioState.ROUTE_BLUETOOTH,
+ EndpointUtils.mapTypeToRoute(CallEndpointCompat.TYPE_BLUETOOTH)
+ )
+ assertEquals(
+ CallAudioState.ROUTE_WIRED_HEADSET,
+ EndpointUtils.mapTypeToRoute(CallEndpointCompat.TYPE_WIRED_HEADSET)
+ )
+ assertEquals(
+ CallAudioState.ROUTE_STREAMING,
+ EndpointUtils.mapTypeToRoute(CallEndpointCompat.TYPE_STREAMING)
+ )
+ assertEquals(
+ CallAudioState.ROUTE_EARPIECE,
+ EndpointUtils.mapTypeToRoute(-1)
+ )
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallsManagerTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallsManagerTest.kt
new file mode 100644
index 0000000..1f0a43a
--- /dev/null
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/CallsManagerTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.os.Build.VERSION_CODES
+import android.telecom.PhoneAccount.CAPABILITY_SELF_MANAGED
+import android.telecom.PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.internal.utils.Utils
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@RequiresApi(VERSION_CODES.O)
+@SdkSuppress(minSdkVersion = VERSION_CODES.O /* api=26 */)
+class CallsManagerTest : BaseTelecomTest() {
+ private val mTestClassName = "androidx.core.telecom.test"
+
+ @SmallTest
+ @Test
+ fun testGetPhoneAccountWithUBuild() {
+ try {
+ Utils.setUtils(TestUtils.mV2Build)
+ val account = mCallsManager.getPhoneAccountHandleForPackage()
+ assertEquals(mTestClassName, account.componentName.className)
+ } finally {
+ Utils.resetUtils()
+ }
+ }
+
+ @SmallTest
+ @Test
+ fun testGetPhoneAccountWithUBuildWithTminusBuild() {
+ try {
+ Utils.setUtils(TestUtils.mBackwardsCompatBuild)
+ val account = mCallsManager.getPhoneAccountHandleForPackage()
+ assertEquals(CallsManager.CONNECTION_SERVICE_CLASS, account.componentName.className)
+ } finally {
+ Utils.resetUtils()
+ }
+ }
+
+ @SmallTest
+ @Test
+ fun testGetPhoneAccountWithInvalidBuild() {
+ try {
+ Utils.setUtils(TestUtils.mInvalidBuild)
+ assertThrows(UnsupportedOperationException::class.java) {
+ mCallsManager.getPhoneAccountHandleForPackage()
+ }
+ } finally {
+ Utils.resetUtils()
+ }
+ }
+
+ @SmallTest
+ @Test
+ fun testRegisterPhoneAccount() {
+ Utils.resetUtils()
+
+ if (Utils.hasInvalidBuildVersion()) {
+ assertThrows(UnsupportedOperationException::class.java) {
+ mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
+ }
+ } else {
+
+ mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
+ val account = mCallsManager.getBuiltPhoneAccount()!!
+
+ if (Utils.hasPlatformV2Apis()) {
+ assertTrue(
+ Utils.hasCapability(
+ CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS,
+ account.capabilities
+ )
+ )
+ } else {
+ assertTrue(
+ account.capabilities and CAPABILITY_SELF_MANAGED ==
+ CAPABILITY_SELF_MANAGED
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/JetpackConnectionServiceTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/JetpackConnectionServiceTest.kt
new file mode 100644
index 0000000..6f7bb4a
--- /dev/null
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/JetpackConnectionServiceTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.os.Build.VERSION_CODES
+import android.telecom.Connection
+import android.telecom.ConnectionRequest
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.TestUtils.Companion.TEST_CALL_ATTRIB_NAME
+import androidx.core.telecom.TestUtils.Companion.TEST_CALL_ATTRIB_NUMBER
+import androidx.core.telecom.internal.CallChannels
+import androidx.core.telecom.internal.JetpackConnectionService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@RequiresApi(VERSION_CODES.O)
+@SdkSuppress(minSdkVersion = VERSION_CODES.O /* api=26 */)
+class JetpackConnectionServiceTest : BaseTelecomTest() {
+ private val callChannels = CallChannels()
+
+ @After
+ fun onDestroy() {
+ super.onDestroyBase()
+ callChannels.closeAllChannels()
+ }
+
+ /**
+ * Ensure an outgoing Connection object has its properties set before sending it off to the
+ * platform. The properties should reflect everything that is set in CallAttributes.
+ */
+ @SmallTest
+ @Test
+ fun testConnectionServicePropertiesAreSet_outgoingCall() {
+ // create the CallAttributes
+ val attributes = TestUtils.createCallAttributes(
+ CallAttributesCompat.DIRECTION_OUTGOING,
+ mPackagePhoneAccountHandle
+ )
+ // simulate the connection being created
+ val connection = mConnectionService.createSelfManagedConnection(
+ createConnectionRequest(attributes),
+ CallAttributesCompat.DIRECTION_OUTGOING
+ )
+ // verify / assert connection properties
+ verifyConnectionPropertiesBasics(connection)
+ assertEquals(Connection.STATE_DIALING, connection!!.state)
+ }
+
+ /**
+ * Ensure an incoming Connection object has its properties set before sending it off to the
+ * platform. The properties should reflect everything that is set in CallAttributes.
+ */
+ @SmallTest
+ @Test
+ fun testConnectionServicePropertiesAreSet_incomingCall() {
+ // create the CallAttributes
+ val attributes = TestUtils.createCallAttributes(
+ CallAttributesCompat.DIRECTION_INCOMING,
+ mPackagePhoneAccountHandle
+ )
+ // simulate the connection being created
+ val connection = mConnectionService.createSelfManagedConnection(
+ createConnectionRequest(attributes),
+ CallAttributesCompat.DIRECTION_INCOMING
+ )
+ // verify / assert connection properties
+ verifyConnectionPropertiesBasics(connection)
+ assertEquals(Connection.STATE_RINGING, connection!!.state)
+ }
+
+ private fun verifyConnectionPropertiesBasics(connection: Connection?) {
+ // assert it's not null
+ assertNotNull(connection)
+ // unwrap for testing
+ val unwrappedConnection = connection!!
+ // assert all the properties are the same
+ assertEquals(TEST_CALL_ATTRIB_NAME, unwrappedConnection.callerDisplayName)
+ assertEquals(TEST_CALL_ATTRIB_NUMBER, unwrappedConnection.address)
+ assertEquals(
+ Connection.CAPABILITY_HOLD,
+ unwrappedConnection.connectionCapabilities
+ and Connection.CAPABILITY_HOLD
+ )
+ assertEquals(
+ Connection.CAPABILITY_SUPPORT_HOLD,
+ unwrappedConnection.connectionCapabilities
+ and Connection.CAPABILITY_SUPPORT_HOLD
+ )
+ assertEquals(0, JetpackConnectionService.mPendingConnectionRequests.size)
+ }
+
+ private fun createConnectionRequest(callAttributesCompat: CallAttributesCompat):
+ ConnectionRequest {
+ // wrap in PendingRequest
+ val pr = JetpackConnectionService.PendingConnectionRequest(
+ callAttributesCompat, callChannels, mWorkerContext, null
+ )
+ // add to the list of pendingRequests
+ JetpackConnectionService.mPendingConnectionRequests.add(pr)
+ // create a ConnectionRequest
+ return ConnectionRequest(mPackagePhoneAccountHandle, TEST_CALL_ATTRIB_NUMBER, null)
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/TestUtils.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/TestUtils.kt
new file mode 100644
index 0000000..d37080f
--- /dev/null
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/TestUtils.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.net.Uri
+import android.telecom.PhoneAccountHandle
+import androidx.core.telecom.internal.utils.BuildVersionAdapter
+
+class TestUtils {
+
+ companion object {
+
+ internal val mV2Build = object : BuildVersionAdapter {
+ override fun hasPlatformV2Apis(): Boolean {
+ return true
+ }
+
+ override fun hasInvalidBuildVersion(): Boolean {
+ return false
+ }
+ }
+
+ internal val mBackwardsCompatBuild = object : BuildVersionAdapter {
+ override fun hasPlatformV2Apis(): Boolean {
+ return false
+ }
+
+ override fun hasInvalidBuildVersion(): Boolean {
+ return false
+ }
+ }
+
+ internal val mInvalidBuild = object : BuildVersionAdapter {
+ override fun hasPlatformV2Apis(): Boolean {
+ return false
+ }
+
+ override fun hasInvalidBuildVersion(): Boolean {
+ return true
+ }
+ }
+
+ val TEST_CALL_ATTRIB_NAME = "Elon Musk"
+ val TEST_CALL_ATTRIB_NUMBER = Uri.parse("tel:6506959001")
+
+ fun createCallAttributes(
+ callDirection: Int,
+ phoneAccountHandle: PhoneAccountHandle,
+ callType: Int? = CallAttributesCompat.CALL_TYPE_AUDIO_CALL,
+ ): CallAttributesCompat {
+
+ val attributes: CallAttributesCompat = if (callType != null) {
+ CallAttributesCompat(
+ TEST_CALL_ATTRIB_NAME,
+ TEST_CALL_ATTRIB_NUMBER,
+ callDirection, callType
+ )
+ } else {
+ CallAttributesCompat(
+ TEST_CALL_ATTRIB_NAME,
+ TEST_CALL_ATTRIB_NUMBER,
+ callDirection
+ )
+ }
+
+ attributes.mHandle = phoneAccountHandle
+
+ return attributes
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/AndroidManifest.xml b/core/core-telecom/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8ea6753
--- /dev/null
+++ b/core/core-telecom/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+
+ <application>
+ <service
+ android:name="androidx.core.telecom.internal.JetpackConnectionService"
+ android:exported="true"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/core/core-telecom/src/main/java/androidx/core/androidx-core-core-telecom-documentation.md b/core/core-telecom/src/main/java/androidx/core/androidx-core-core-telecom-documentation.md
new file mode 100644
index 0000000..bfb5ecc
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/androidx-core-core-telecom-documentation.md
@@ -0,0 +1,7 @@
+# Module root
+
+<GROUPID> <ARTIFACTID>
+
+# Package androidx.core.telecom
+
+TODO: Document
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallAttributesCompat.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallAttributesCompat.kt
new file mode 100644
index 0000000..e3d3610
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallAttributesCompat.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.net.Uri
+import android.telecom.PhoneAccountHandle
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.core.telecom.internal.utils.CallAttributesUtils
+import androidx.core.telecom.internal.utils.Utils
+import java.util.Objects
+
+/**
+ * CallAttributes represents a set of properties that define a new Call. Applications should build
+ * an instance of this class and use [CallsManager.addCall] to start a new call with Telecom.
+ *
+ * @param displayName Display name of the person on the other end of the call
+ * @param address Address of the call. Note, this can be extended to a meeting link
+ * @param direction The direction (Outgoing/Incoming) of the new Call
+ * @param callType Information related to data being transmitted (voice, video, etc. )
+ * @param callCapabilities Allows a package to opt into capabilities on the telecom side,
+ * on a per-call basis
+ */
+class CallAttributesCompat constructor(
+ val displayName: CharSequence,
+ val address: Uri,
+ @Direction val direction: Int,
+ @CallType val callType: Int = CALL_TYPE_AUDIO_CALL,
+ @CallCapability val callCapabilities: Int = SUPPORTS_SET_INACTIVE
+) {
+ internal var mHandle: PhoneAccountHandle? = null
+
+ override fun toString(): String {
+ return "CallAttributes(" +
+ "displayName=[$displayName], " +
+ "address=[$address], " +
+ "direction=[${directionToString()}], " +
+ "callType=[${callTypeToString()}], " +
+ "capabilities=[${capabilitiesToString()}])"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is CallAttributesCompat &&
+ displayName == other.displayName &&
+ address == other.address &&
+ direction == other.direction &&
+ callType == other.callType &&
+ callCapabilities == other.callCapabilities
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(displayName, address, direction, callType, callCapabilities)
+ }
+
+ companion object {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(DIRECTION_INCOMING, DIRECTION_OUTGOING)
+ @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
+ annotation class Direction
+
+ /**
+ * Indicates that the call is an incoming call.
+ */
+ const val DIRECTION_INCOMING = 1
+
+ /**
+ * Indicates that the call is an outgoing call.
+ */
+ const val DIRECTION_OUTGOING = 2
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(CALL_TYPE_AUDIO_CALL, CALL_TYPE_VIDEO_CALL)
+ @Target(AnnotationTarget.TYPE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY)
+ annotation class CallType
+
+ /**
+ * Used when answering or dialing a call to indicate that the call does not have a video
+ * component
+ */
+ const val CALL_TYPE_AUDIO_CALL = 1
+
+ /**
+ * Indicates video transmission is supported
+ */
+ const val CALL_TYPE_VIDEO_CALL = 2
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER, flag = true)
+ @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
+ annotation class CallCapability
+
+ /**
+ * This call being created can be set to inactive (traditionally referred to as hold). This
+ * means that once a new call goes active, if the active call needs to be held in order to
+ * place or receive an incoming call, the active call will be placed on hold. otherwise,
+ * the active call may be disconnected.
+ */
+ const val SUPPORTS_SET_INACTIVE = 1 shl 1
+
+ /**
+ * This call can be streamed from a root device to another device to continue the call
+ * without completely transferring it. The call continues to take place on the source
+ * device, however media and control are streamed to another device.
+ */
+ const val SUPPORTS_STREAM = 1 shl 2
+
+ /**
+ * This call can be completely transferred from one endpoint to another.
+ */
+ const val SUPPORTS_TRANSFER = 1 shl 3
+ }
+
+ @RequiresApi(34)
+ internal fun toCallAttributes(
+ phoneAccountHandle: PhoneAccountHandle
+ ): android.telecom.CallAttributes {
+ return CallAttributesUtils.Api34PlusImpl.toTelecomCallAttributes(
+ phoneAccountHandle,
+ direction,
+ displayName,
+ address,
+ callType,
+ callCapabilities
+ )
+ }
+
+ private fun directionToString(): String {
+ return if (direction == DIRECTION_OUTGOING) {
+ "Outgoing"
+ } else {
+ "Incoming"
+ }
+ }
+
+ private fun callTypeToString(): String {
+ return if (callType == CALL_TYPE_AUDIO_CALL) {
+ "Audio"
+ } else {
+ "Video"
+ }
+ }
+
+ internal fun hasSupportsSetInactiveCapability(): Boolean {
+ return Utils.hasCapability(SUPPORTS_SET_INACTIVE, callCapabilities)
+ }
+
+ private fun hasStreamCapability(): Boolean {
+ return Utils.hasCapability(SUPPORTS_STREAM, callCapabilities)
+ }
+
+ private fun hasTransferCapability(): Boolean {
+ return Utils.hasCapability(SUPPORTS_TRANSFER, callCapabilities)
+ }
+
+ private fun capabilitiesToString(): String {
+ val sb = StringBuilder()
+ sb.append("[")
+ if (hasSupportsSetInactiveCapability()) {
+ sb.append("SetInactive")
+ }
+ if (hasStreamCapability()) {
+ sb.append(", Stream")
+ }
+ if (hasTransferCapability()) {
+ sb.append(", Transfer")
+ }
+ sb.append("])")
+ return sb.toString()
+ }
+
+ internal fun isOutgoingCall(): Boolean {
+ return direction == DIRECTION_OUTGOING
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallControlCallback.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlCallback.kt
new file mode 100644
index 0000000..15bc1d5
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlCallback.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+/**
+ * CallControlCallback relays call updates (that require a response) from the Telecom framework out
+ * to the application. This can include operations which the app must implement on a Call due to the
+ * presence of other calls on the device, requests relayed from a Bluetooth device, or from another
+ * calling surface.
+ *
+ * <p>
+ * All CallControlCallbacks are transactional, meaning that a client must
+ * complete the suspend fun with a [Boolean] response in order to complete the
+ * CallControlCallback. If the operation has been completed, the [suspend fun] should return
+ * true. Otherwise, the suspend fun should be returned with a false to represent the
+ * CallControlCallback cannot be completed on the client side.
+ *
+ * <p>
+ * Note: Each CallEventCallback has a timeout of 5000 milliseconds. Failing to complete the
+ * suspend fun before the timeout will result in a failed transaction.
+ */
+interface CallControlCallback {
+ /**
+ * Telecom is informing your VoIP application to set the call active. Telecom is requesting
+ * this on behalf of an system service (e.g. Automotive service) or a device (e.g. Wearable).
+ *
+ * @return true to indicate your VoIP application can set the call (that corresponds to this
+ * CallControlCallback) to active. Otherwise, return false to indicate your application is
+ * unable to process the request and telecom will cancel the external request.
+ */
+ suspend fun onSetActive(): Boolean
+
+ /**
+ * Telecom is informing your VoIP application to set the call inactive. This is the same as
+ * holding a call for two endpoints but can be extended to setting a meeting inactive. Telecom
+ * is requesting this on behalf of an system service (e.g. Automotive service) or a device (e.g.
+ * Wearable).
+ *
+ * Note: Your app must stop using the microphone and playing incoming media when returning.
+ *
+ * @return true to indicate your VoIP application can transition the call state to inactive.
+ * Otherwise, return false to indicate your application is unable to process the request and
+ * telecom will cancel the external request.
+ */
+ suspend fun onSetInactive(): Boolean
+
+ /**
+ * Telecom is informing your VoIP application to answer an incoming call and set it to active.
+ * Telecom is requesting this on behalf of an system service (e.g. Automotive service) or a
+ * device (e.g. Wearable).
+ *
+ * @param callType that call is requesting to be answered as.
+ *
+ * @return true to indicate your VoIP application can answer the call with the given
+ * [CallAttributesCompat.Companion.CallType]. Otherwise, return false to indicate your application is
+ * unable to process the request and telecom will cancel the external request.
+ */
+ suspend fun onAnswer(@CallAttributesCompat.Companion.CallType callType: Int): Boolean
+
+ /**
+ * Telecom is informing your VoIP application to disconnect the call. Telecom is requesting this
+ * on behalf of an system service (e.g. Automotive service) or a device (e.g. Wearable).
+ *
+ * @param disconnectCause represents the cause for disconnecting the call.
+ *
+ * @return true when your VoIP application has disconnected the call. Otherwise, return false to
+ * indicate your application is unable to process the request. However, telecom will still
+ * disconnect and untrack the call.
+ */
+ suspend fun onDisconnect(disconnectCause: android.telecom.DisconnectCause): Boolean
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt
new file mode 100644
index 0000000..e72eb6e
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.os.ParcelUuid
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * DSL interface to provide and receive updates about a single call session. The scope should be
+ * used to provide updates to the call state and receive updates about a call state. Example usage:
+ * <pre>
+ * // initiate a call and control via the CallControlScope
+ * mCallsManager.addCall(callAttributes) { // This block represents the CallControlScope
+ *
+ * // set your implementation of [CallControlCallback]
+ * setCallback(myCallControlCallbackImplementation)
+ *
+ * // UI flow sends an update to a call state, relay the update to Telecom
+ * disconnectCallButton.setOnClickListener {
+ * val wasSuccessful = disconnect(reason) // waits for telecom async. response
+ * // update UI
+ * }
+ *
+ * // Collect updates
+ * currentCallEndpoint
+ * .onEach { // access the new [CallEndpoint] here }
+ * .launchIn(coroutineScope)
+ * }
+ * <pre>
+ */
+interface CallControlScope {
+ /**
+ * This method should be the first method called within the [CallControlScope] and your VoIP
+ * application should pass in a valid implementation of [CallControlCallback].
+ *
+ * <p>
+ * Failing to call this API may result in your VoIP process being killed or an error to occur.
+ */
+ @Suppress("ExecutorRegistration")
+ fun setCallback(callControlCallback: CallControlCallback)
+
+ /**
+ * @return the 128-bit universally unique identifier Telecom assigned to this CallControlScope.
+ * This id can be helpful for debugging when dumping the telecom system.
+ */
+ fun getCallId(): ParcelUuid
+
+ /**
+ * Inform Telecom that your app wants to make this call active. This method should be called
+ * when either an outgoing call is ready to go active or a held call is ready to go active
+ * again. For incoming calls that are ready to be answered, use [answer].
+ *
+ * Telecom will return true if your app is able to set the call active. Otherwise false will
+ * be returned (ex. another call is active and telecom cannot set this call active until the
+ * other call is held or disconnected)
+ */
+ suspend fun setActive(): Boolean
+
+ /**
+ * Inform Telecom that your app wants to make this call inactive. This the same as hold for two
+ * call endpoints but can be extended to setting a meeting to inactive.
+ *
+ * Telecom will return true if your app is able to set the call inactive. Otherwise, false will
+ * be returned.
+ */
+ suspend fun setInactive(): Boolean
+
+ /**
+ * Inform Telecom that your app wants to make this incoming call active. For outgoing calls
+ * and calls that have been placed on hold, use [setActive].
+ *
+ * @param [callType] that call is to be answered as.
+ *
+ * Telecom will return true if your app is able to answer the call. Otherwise false will
+ * be returned (ex. another call is active and telecom cannot set this call active until the
+ * other call is held or disconnected) which means that your app cannot answer this call at
+ * this time.
+ */
+ suspend fun answer(@CallAttributesCompat.Companion.CallType callType: Int): Boolean
+
+ /**
+ * Inform Telecom that your app wishes to disconnect the call and remove the call from telecom
+ * tracking.
+ *
+ * @param disconnectCause represents the cause for disconnecting the call. The only valid
+ * codes for the [android.telecom.DisconnectCause] passed in are:
+ * <ul>
+ * <li>[DisconnectCause#LOCAL]</li>
+ * <li>[DisconnectCause#REMOTE]</li>
+ * <li>[DisconnectCause#REJECTED]</li>
+ * <li>[DisconnectCause#MISSED]</li>
+ * </ul>
+ *
+ * Telecom will always return true unless the call has already been disconnected.
+ *
+ * <p>
+ * Note: After the call has been successfully disconnected, calling any [CallControlScope] will
+ * result in a false to be returned.
+ */
+ suspend fun disconnect(disconnectCause: android.telecom.DisconnectCause): Boolean
+
+ /**
+ * Request a [CallEndpointCompat] change. Clients should not define their own [CallEndpointCompat] when
+ * requesting a change. Instead, the new [endpoint] should be one of the valid [CallEndpointCompat]s
+ * provided by [availableEndpoints].
+ *
+ * @param endpoint The [CallEndpointCompat] to change to.
+ *
+ * Telecom will return true if your app is able to switch to the requested new endpoint.
+ * Otherwise false will be returned.
+ */
+ suspend fun requestEndpointChange(endpoint: CallEndpointCompat): Boolean
+
+ /**
+ * Collect the new [CallEndpointCompat] through which call media flows (i.e. speaker,
+ * bluetooth, etc.).
+ */
+ val currentCallEndpoint: Flow<CallEndpointCompat>
+
+ /**
+ * Collect the set of available [CallEndpointCompat]s reported by Telecom.
+ */
+ val availableEndpoints: Flow<List<CallEndpointCompat>>
+
+ /**
+ * Collect the current mute state of the call. This Flow is updated every time the mute state
+ * changes.
+ */
+ val isMuted: Flow<Boolean>
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallEndpointCompat.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallEndpointCompat.kt
new file mode 100644
index 0000000..81aa731
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallEndpointCompat.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.os.Build.VERSION_CODES
+import android.os.ParcelUuid
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.core.telecom.internal.utils.EndpointUtils
+import java.util.Objects
+import java.util.UUID
+
+/**
+ * Constructor for a [CallEndpointCompat] object.
+ *
+ * @param name Human-readable name associated with the endpoint
+ * @param type The type of endpoint through which call media being routed
+ * Allowed values:
+ * [.TYPE_EARPIECE]
+ * [.TYPE_BLUETOOTH]
+ * [.TYPE_WIRED_HEADSET]
+ * [.TYPE_SPEAKER]
+ * [.TYPE_STREAMING]
+ * [.TYPE_UNKNOWN]
+ * @param identifier A unique identifier for this endpoint on the device
+ */
+@RequiresApi(VERSION_CODES.O)
+class CallEndpointCompat(val name: CharSequence, val type: Int, val identifier: ParcelUuid) {
+ internal var mMackAddress: String = "-1"
+
+ override fun toString(): String {
+ return "CallEndpoint(" +
+ "name=[$name]," +
+ "type=[${EndpointUtils.endpointTypeToString(type)}]," +
+ "identifier=[$identifier])"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is CallEndpointCompat &&
+ name == other.name &&
+ type == other.type &&
+ identifier == other.identifier
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(name, type, identifier)
+ }
+
+ companion object {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(
+ TYPE_UNKNOWN,
+ TYPE_EARPIECE,
+ TYPE_BLUETOOTH,
+ TYPE_WIRED_HEADSET,
+ TYPE_SPEAKER,
+ TYPE_STREAMING
+ )
+ @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
+ annotation class EndpointType
+
+ /** Indicates that the type of endpoint through which call media flows is unknown type. */
+ const val TYPE_UNKNOWN = -1
+
+ /** Indicates that the type of endpoint through which call media flows is an earpiece. */
+ const val TYPE_EARPIECE = 1
+
+ /** Indicates that the type of endpoint through which call media flows is a Bluetooth. */
+ const val TYPE_BLUETOOTH = 2
+
+ /** Indicates that the type of endpoint through which call media flows is a wired headset. */
+ const val TYPE_WIRED_HEADSET = 3
+
+ /** Indicates that the type of endpoint through which call media flows is a speakerphone. */
+ const val TYPE_SPEAKER = 4
+
+ /** Indicates that the type of endpoint through which call media flows is an external. */
+ const val TYPE_STREAMING = 5
+ }
+
+ internal constructor(name: String, @EndpointType type: Int) :
+ this(name, type, ParcelUuid(UUID.randomUUID())) {
+ }
+
+ internal constructor(name: String, @EndpointType type: Int, address: String) : this(
+ name,
+ type
+ ) {
+ mMackAddress = address
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallException.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallException.kt
new file mode 100644
index 0000000..8677df2
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallException.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+
+/**
+ * This class defines exceptions that can be thrown when using [androidx.core.telecom] APIs.
+ */
+class CallException(
+ @CallErrorCode val code: Int = ERROR_UNKNOWN_CODE,
+ message: String? = codeToMessage(code)
+) : RuntimeException(message) {
+
+ override fun toString(): String {
+ return "CallException( code=[$code], message=[$message])"
+ }
+
+ companion object {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(ERROR_UNKNOWN_CODE, ERROR_CALLBACKS_CODE)
+ annotation class CallErrorCode
+
+ /**
+ * The operation has failed due to an unknown or unspecified error.
+ */
+ const val ERROR_UNKNOWN_CODE = 1
+
+ /**
+ * This error code is thrown whenever a call is added via [CallsManager.addCall] and the
+ * [CallControlScope.setCallback]s is not the first API called in the session block or at
+ * all. In order to avoid this exception, ensure your [CallControlScope] is calling
+ * [CallControlScope.setCallback]s.
+ */
+ const val ERROR_CALLBACKS_CODE = 2
+
+ internal const val ERROR_CALLBACKS_MSG: String = "Error, when using the " +
+ "[CallControlScope], you must first set the " +
+ "[androidx.core.telecom.CallControlCallback]s via [CallControlScope]#[setCallback]"
+
+ internal const val ERROR_BUILD_VERSION: String = "Core-Telecom only supports builds from" +
+ " Oreo (Android 8) and above. In order to utilize Core-Telecom, your device must" +
+ " be updated."
+
+ internal fun codeToMessage(@CallErrorCode code: Int): String {
+ when (code) {
+ ERROR_CALLBACKS_CODE -> return ERROR_CALLBACKS_MSG
+ }
+ return "An Unknown Error has occurred while using the Core-Telecom APIs"
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
new file mode 100644
index 0000000..25e9287
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom
+
+import android.content.ComponentName
+import android.content.Context
+import android.os.Build.VERSION_CODES
+import android.os.OutcomeReceiver
+import android.os.Process
+import android.telecom.CallControl
+import android.telecom.CallException
+import android.telecom.PhoneAccount
+import android.telecom.PhoneAccountHandle
+import android.telecom.TelecomManager
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.telecom.CallAttributesCompat.Companion.CALL_TYPE_VIDEO_CALL
+import androidx.core.telecom.internal.CallChannels
+import androidx.core.telecom.internal.CallSession
+import androidx.core.telecom.internal.CallSessionLegacy
+import androidx.core.telecom.internal.JetpackConnectionService
+import androidx.core.telecom.internal.utils.Utils
+import java.util.concurrent.CancellationException
+import java.util.concurrent.Executor
+import kotlin.coroutines.coroutineContext
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.job
+
+/**
+ * CallsManager allows VoIP applications to add their calls to the Android system service Telecom.
+ * By doing this, other services are aware of your VoIP application calls which leads to a more
+ * stable environment. For example, a wearable may be able to answer an incoming call from your
+ * application if the call is added to the Telecom system. VoIP applications that manage calls and
+ * do not inform the Telecom system may experience issues with resources (ex. microphone access).
+ *
+ * Note that access to some telecom information is permission-protected. Your app cannot access the
+ * protected information or gain access to protected functionality unless it has the appropriate
+ * permissions declared in its manifest file. Where permissions apply, they are noted in the method
+ * descriptions.
+ */
+@RequiresApi(VERSION_CODES.O)
+class CallsManager constructor(context: Context) {
+ private val mContext: Context = context
+ private var mPhoneAccount: PhoneAccount? = null
+ private val mTelecomManager: TelecomManager =
+ mContext.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
+ internal val mConnectionService: JetpackConnectionService = JetpackConnectionService()
+
+ // A single declared constant for a direct [Executor], since the coroutines primitives we invoke
+ // from the associated callbacks will perform their own dispatch as needed.
+ private val mDirectExecutor = Executor { it.run() }
+
+ companion object {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+ @IntDef(
+ CAPABILITY_BASELINE,
+ CAPABILITY_SUPPORTS_VIDEO_CALLING,
+ CAPABILITY_SUPPORTS_CALL_STREAMING,
+ flag = true
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class Capability
+
+ /**
+ * If your VoIP application does not want support any of the capabilities below, then your
+ * application can register with [CAPABILITY_BASELINE].
+ *
+ * Note: Calls can still be added and to the Telecom system but if other services request to
+ * perform a capability that is not supported by your application, Telecom will notify the
+ * service of the inability to perform the action instead of hitting an error.
+ */
+ const val CAPABILITY_BASELINE = 1 shl 0
+
+ /**
+ * Flag indicating that your VoIP application supports video calling.
+ * This is not an indication that your application is currently able to make a video
+ * call, but rather that it has the ability to make video calls (but not necessarily at this
+ * time).
+ *
+ * Whether a call can make a video call is ultimately controlled by
+ * [androidx.core.telecom.CallAttributesCompat]s capability
+ * [androidx.core.telecom.CallAttributesCompat.CallType]#[CALL_TYPE_VIDEO_CALL],
+ * which indicates that particular call is currently capable of making a video call.
+ */
+ const val CAPABILITY_SUPPORTS_VIDEO_CALLING = 1 shl 1
+
+ /**
+ * Flag indicating that this VoIP application supports call streaming. Call streaming means
+ * a call can be streamed from a root device to another device to continue the call
+ * without completely transferring it. The call continues to take place on the source
+ * device, however media and control are streamed to another device.
+ * [androidx.core.telecom.CallAttributesCompat.CallType]#[CAPABILITY_SUPPORTS_CALL_STREAMING]
+ * must also be set on per call basis in the event an application wants to gate this
+ * capability on a stricter basis.
+ */
+ const val CAPABILITY_SUPPORTS_CALL_STREAMING = 1 shl 2
+
+ // identifiers that indicate the call was established with core-telecom
+ internal const val PACKAGE_HANDLE_ID: String = "Jetpack"
+ internal const val PACKAGE_LABEL: String = "Telecom-Jetpack"
+ internal const val CONNECTION_SERVICE_CLASS =
+ "androidx.core.telecom.internal.JetpackConnectionService"
+ // fail messages specific to addCall
+ internal const val CALL_CREATION_FAILURE_MSG =
+ "The call failed to be added."
+ }
+
+ /**
+ * VoIP applications should look at each [Capability] annotated above and call this API in
+ * order to start adding calls via [addCall].
+ *
+ * Note: Registering capabilities must be done before calling [addCall] or an exception will
+ * be thrown by [addCall].
+ *
+ * @Throws UnsupportedOperationException if the device is on an invalid build
+ */
+ @RequiresPermission(value = "android.permission.MANAGE_OWN_CALLS")
+ fun registerAppWithTelecom(@Capability capabilities: Int) {
+ // verify the build version supports this API and throw an exception if not
+ Utils.verifyBuildVersion()
+ // start to build the PhoneAccount that will be registered via the platform API
+ var platformCapabilities: Int = PhoneAccount.CAPABILITY_SELF_MANAGED
+ val phoneAccountBuilder = PhoneAccount.builder(
+ getPhoneAccountHandleForPackage(),
+ PACKAGE_LABEL
+ )
+ // append additional capabilities if the device is on a U build or above
+ if (Utils.hasPlatformV2Apis()) {
+ platformCapabilities = PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS or
+ Utils.remapJetpackCapabilitiesToPlatformCapabilities(capabilities)
+ }
+ // remap and set capabilities
+ phoneAccountBuilder.setCapabilities(platformCapabilities)
+ // build and register the PhoneAccount via the Platform API
+ mPhoneAccount = phoneAccountBuilder.build()
+ mTelecomManager.registerPhoneAccount(mPhoneAccount)
+ }
+
+ /**
+ * Adds a new call with the specified [CallAttributesCompat] to the telecom service. This method
+ * can be used to add both incoming and outgoing calls.
+ *
+ * @param callAttributes attributes of the new call (incoming or outgoing, address, etc. )
+ * @param block DSL interface block that will run when the call is ready
+ *
+ * @Throws UnsupportedOperationException if the device is on an invalid build
+ * @Throws CancellationException if the call failed to be added
+ * @Throws CallException if [CallControlScope.setCallback] is not called first within the block
+ */
+ @RequiresPermission(value = "android.permission.MANAGE_OWN_CALLS")
+ @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+ @Suppress("ClassVerificationFailure")
+ suspend fun addCall(
+ callAttributes: CallAttributesCompat,
+ block: CallControlScope.() -> Unit
+ ) {
+ // This API is not supported for device running anything below Android O (26)
+ Utils.verifyBuildVersion()
+ // Setup channels for the CallEventCallbacks that only provide info updates
+ val callChannels = CallChannels()
+ callAttributes.mHandle = getPhoneAccountHandleForPackage()
+
+ // create a call session based off the build version
+ @RequiresApi(34)
+ if (Utils.hasPlatformV2Apis()) {
+ // CompletableDeferred pauses the execution of this method until the CallControl is
+ // returned by the Platform.
+ val openResult = CompletableDeferred<CallSession>(parent = coroutineContext.job)
+ // CallSession is responsible for handling both CallControl responses from the Platform
+ // and propagates CallControlCallbacks that originate in the Platform out to the client.
+ val callSession = CallSession(coroutineContext)
+
+ /**
+ * The Platform [android.telecom.TelecomManager.addCall] requires a
+ * [OutcomeReceiver]#<[CallControl], [CallException]> that will receive the async
+ * response of whether the call can be added.
+ */
+ val callControlOutcomeReceiver =
+ object : OutcomeReceiver<CallControl, CallException> {
+ override fun onResult(control: CallControl) {
+ callSession.setCallControl(control)
+ openResult.complete(callSession)
+ }
+
+ override fun onError(reason: CallException) {
+ // close all channels
+ callChannels.closeAllChannels()
+ // fail if we were still waiting for a CallControl
+ openResult.cancel(CancellationException(CALL_CREATION_FAILURE_MSG))
+ }
+ }
+
+ // leverage the platform API
+ mTelecomManager.addCall(
+ callAttributes.toCallAttributes(getPhoneAccountHandleForPackage()),
+ mDirectExecutor,
+ callControlOutcomeReceiver,
+ CallSession.CallControlCallbackImpl(callSession),
+ CallSession.CallEventCallbackImpl(callChannels)
+ )
+
+ openResult.await() /* wait for the platform to provide a CallControl object */
+ /* at this point in time we have CallControl object */
+ val scope =
+ CallSession.CallControlScopeImpl(openResult.getCompleted(), callChannels)
+
+ // Run the clients code with the session active and exposed via the CallControlScope
+ // interface implementation declared above.
+ scope.block()
+ } else {
+ // CompletableDeferred pauses the execution of this method until the Connection
+ // is created in JetpackConnectionService
+ val openResult =
+ CompletableDeferred<CallSessionLegacy>(parent = coroutineContext.job)
+
+ mConnectionService.createConnectionRequest(
+ mTelecomManager,
+ JetpackConnectionService.PendingConnectionRequest(
+ callAttributes, callChannels, coroutineContext, openResult
+ )
+ )
+
+ openResult.await()
+
+ val scope =
+ CallSessionLegacy.CallControlScopeImpl(openResult.getCompleted(), callChannels)
+
+ // Run the clients code with the session active and exposed via the
+ // CallControlScope interface implementation declared above.
+ scope.block()
+ }
+ }
+
+ internal fun getPhoneAccountHandleForPackage(): PhoneAccountHandle {
+ // This API is not supported for device running anything below Android O (26)
+ Utils.verifyBuildVersion()
+
+ val className = if (Utils.hasPlatformV2Apis()) {
+ mContext.packageName
+ } else {
+ CONNECTION_SERVICE_CLASS
+ }
+ return PhoneAccountHandle(
+ ComponentName(mContext.packageName, className),
+ PACKAGE_HANDLE_ID,
+ Process.myUserHandle()
+ )
+ }
+
+ internal fun getBuiltPhoneAccount(): PhoneAccount? {
+ return mPhoneAccount
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallChannels.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallChannels.kt
new file mode 100644
index 0000000..8989ca4
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallChannels.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal
+
+import androidx.core.telecom.CallEndpointCompat
+import kotlinx.coroutines.channels.Channel
+
+internal class CallChannels(
+ val currentEndpointChannel: Channel<CallEndpointCompat> = Channel(Channel.UNLIMITED),
+ val availableEndpointChannel: Channel<List<CallEndpointCompat>> = Channel(Channel.UNLIMITED),
+ val isMutedChannel: Channel<Boolean> = Channel(Channel.UNLIMITED)
+) {
+ fun closeAllChannels() {
+ currentEndpointChannel.close()
+ availableEndpointChannel.close()
+ isMutedChannel.close()
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
new file mode 100644
index 0000000..a0e2cae
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal
+
+import android.os.Bundle
+import android.os.OutcomeReceiver
+import android.os.ParcelUuid
+import android.telecom.CallException
+import android.telecom.DisconnectCause
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallControlCallback
+import androidx.core.telecom.CallControlScope
+import androidx.core.telecom.CallEndpointCompat
+import androidx.core.telecom.internal.utils.EndpointUtils
+import java.util.function.Consumer
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+@RequiresApi(34)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@Suppress("ClassVerificationFailure")
+internal class CallSession(coroutineContext: CoroutineContext) {
+ private val mCoroutineContext = coroutineContext
+ private var mPlatformInterface: android.telecom.CallControl? = null
+ private var mClientInterface: CallControlCallback? = null
+
+ class CallControlCallbackImpl(private val callSession: CallSession) :
+ android.telecom.CallControlCallback {
+ override fun onSetActive(wasCompleted: Consumer<Boolean>) {
+ callSession.onSetActive(wasCompleted)
+ }
+
+ override fun onSetInactive(wasCompleted: Consumer<Boolean>) {
+ callSession.onSetInactive(wasCompleted)
+ }
+
+ override fun onAnswer(videoState: Int, wasCompleted: Consumer<Boolean>) {
+ callSession.onAnswer(videoState, wasCompleted)
+ }
+
+ override fun onDisconnect(
+ disconnectCause: DisconnectCause,
+ wasCompleted: Consumer<Boolean>
+ ) {
+ callSession.onDisconnect(disconnectCause, wasCompleted)
+ }
+
+ override fun onCallStreamingStarted(wasCompleted: Consumer<Boolean>) {
+ TODO("Implement with the CallStreaming code")
+ }
+ }
+
+ class CallEventCallbackImpl(private val callChannels: CallChannels) :
+ android.telecom.CallEventCallback {
+ override fun onCallEndpointChanged(
+ endpoint: android.telecom.CallEndpoint
+ ) {
+ callChannels.currentEndpointChannel.trySend(
+ EndpointUtils.Api34PlusImpl.toCallEndpointCompat(endpoint)
+ ).getOrThrow()
+ }
+
+ override fun onAvailableCallEndpointsChanged(
+ endpoints: List<android.telecom.CallEndpoint>
+ ) {
+ callChannels.availableEndpointChannel.trySend(
+ EndpointUtils.Api34PlusImpl.toCallEndpointsCompat(endpoints)
+ ).getOrThrow()
+ }
+
+ override fun onMuteStateChanged(isMuted: Boolean) {
+ callChannels.isMutedChannel.trySend(isMuted).getOrThrow()
+ }
+
+ override fun onCallStreamingFailed(reason: Int) {
+ TODO("Implement with the CallStreaming code")
+ }
+
+ override fun onEvent(event: String, extras: Bundle) {
+ TODO("Implement when events are agreed upon by ICS and package")
+ }
+ }
+
+ /**
+ * CallControl is set by CallsManager#addCall when the CallControl object is returned by the
+ * platform
+ */
+ fun setCallControl(control: android.telecom.CallControl) {
+ mPlatformInterface = control
+ }
+
+ /**
+ * pass in the clients callback implementation for CallControlCallback that is set in the
+ * CallsManager#addCall scope.
+ */
+ fun setCallControlCallback(clientCallbackImpl: CallControlCallback) {
+ mClientInterface = clientCallbackImpl
+ }
+
+ fun hasClientSetCallbacks(): Boolean {
+ return mClientInterface != null
+ }
+
+ /**
+ * Custom OutcomeReceiver that handles the Platform responses to a CallControl API call
+ */
+ inner class CallControlReceiver(deferred: CompletableDeferred<Boolean>) :
+ OutcomeReceiver<Void, CallException> {
+ private val mResultDeferred: CompletableDeferred<Boolean> = deferred
+
+ override fun onResult(r: Void?) {
+ mResultDeferred.complete(true)
+ }
+
+ override fun onError(error: CallException) {
+ mResultDeferred.complete(false)
+ }
+ }
+
+ fun getCallId(): ParcelUuid {
+ return mPlatformInterface!!.callId
+ }
+
+ suspend fun setActive(): Boolean {
+ val result: CompletableDeferred<Boolean> = CompletableDeferred()
+ mPlatformInterface?.setActive(Runnable::run, CallControlReceiver(result))
+ result.await()
+ return result.getCompleted()
+ }
+
+ suspend fun setInactive(): Boolean {
+ val result: CompletableDeferred<Boolean> = CompletableDeferred()
+ mPlatformInterface?.setInactive(Runnable::run, CallControlReceiver(result))
+ result.await()
+ return result.getCompleted()
+ }
+
+ suspend fun answer(videoState: Int): Boolean {
+ val result: CompletableDeferred<Boolean> = CompletableDeferred()
+ mPlatformInterface?.answer(videoState, Runnable::run, CallControlReceiver(result))
+ result.await()
+ return result.getCompleted()
+ }
+
+ suspend fun requestEndpointChange(endpoint: android.telecom.CallEndpoint): Boolean {
+ val result: CompletableDeferred<Boolean> = CompletableDeferred()
+ mPlatformInterface?.requestCallEndpointChange(
+ endpoint,
+ Runnable::run, CallControlReceiver(result)
+ )
+ result.await()
+ return result.getCompleted()
+ }
+
+ suspend fun disconnect(disconnectCause: DisconnectCause): Boolean {
+ val result: CompletableDeferred<Boolean> = CompletableDeferred()
+ mPlatformInterface?.disconnect(
+ disconnectCause,
+ Runnable::run,
+ CallControlReceiver(result)
+ )
+ result.await()
+ return result.getCompleted()
+ }
+
+ /**
+ * CallControlCallback
+ */
+ fun onSetActive(wasCompleted: Consumer<Boolean>) {
+ CoroutineScope(mCoroutineContext).launch {
+ val clientResponse: Boolean = mClientInterface!!.onSetActive()
+ wasCompleted.accept(clientResponse)
+ }
+ }
+
+ fun onSetInactive(wasCompleted: Consumer<Boolean>) {
+ CoroutineScope(mCoroutineContext).launch {
+ val clientResponse: Boolean = mClientInterface!!.onSetInactive()
+ wasCompleted.accept(clientResponse)
+ }
+ }
+
+ fun onAnswer(videoState: Int, wasCompleted: Consumer<Boolean>) {
+ CoroutineScope(mCoroutineContext).launch {
+ val clientResponse: Boolean = mClientInterface!!.onAnswer(videoState)
+ wasCompleted.accept(clientResponse)
+ }
+ }
+
+ fun onDisconnect(cause: DisconnectCause, wasCompleted: Consumer<Boolean>) {
+ CoroutineScope(mCoroutineContext).launch {
+ val clientResponse: Boolean = mClientInterface!!.onDisconnect(cause)
+ wasCompleted.accept(clientResponse)
+ }
+ }
+
+ /**
+ * =========================================================================================
+ * Simple implementation of [CallControlScope] with a [CallSession] as the session.
+ * =========================================================================================
+ */
+ class CallControlScopeImpl(
+ private val session: CallSession,
+ callChannels: CallChannels
+ ) : CallControlScope {
+ // handle actionable/handshake events that originate in the platform
+ // and require a response from the client
+ override fun setCallback(callControlCallback: CallControlCallback) {
+ session.setCallControlCallback(callControlCallback)
+ }
+
+ // handle requests that originate from the client and propagate into platform
+ // return the platforms response which indicates success of the request.
+ override fun getCallId(): ParcelUuid {
+ verifySessionCallbacks()
+ return session.getCallId()
+ }
+
+ override suspend fun setActive(): Boolean {
+ verifySessionCallbacks()
+ return session.setActive()
+ }
+
+ override suspend fun setInactive(): Boolean {
+ verifySessionCallbacks()
+ return session.setInactive()
+ }
+
+ override suspend fun answer(callType: Int): Boolean {
+ verifySessionCallbacks()
+ return session.answer(callType)
+ }
+
+ override suspend fun disconnect(disconnectCause: DisconnectCause): Boolean {
+ verifySessionCallbacks()
+ return session.disconnect(disconnectCause)
+ }
+
+ override suspend fun requestEndpointChange(endpoint: CallEndpointCompat):
+ Boolean {
+ verifySessionCallbacks()
+ return session.requestEndpointChange(
+ EndpointUtils.Api34PlusImpl.toCallEndpoint(endpoint)
+ )
+ }
+
+ // Send these events out to the client to collect
+ override val currentCallEndpoint: Flow<CallEndpointCompat> =
+ callChannels.currentEndpointChannel.receiveAsFlow()
+
+ override val availableEndpoints: Flow<List<CallEndpointCompat>> =
+ callChannels.availableEndpointChannel.receiveAsFlow()
+
+ override val isMuted: Flow<Boolean> =
+ callChannels.isMutedChannel.receiveAsFlow()
+
+ private fun verifySessionCallbacks() {
+ if (!session.hasClientSetCallbacks()) {
+ throw androidx.core.telecom.CallException(
+ androidx.core.telecom.CallException.ERROR_CALLBACKS_CODE)
+ }
+ }
+ }
+}
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
new file mode 100644
index 0000000..3a058dd
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal
+
+import android.bluetooth.BluetoothDevice
+import android.os.Build
+import android.os.Build.VERSION_CODES
+import android.os.ParcelUuid
+import android.telecom.CallAudioState
+import android.telecom.DisconnectCause
+import android.util.Log
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallControlCallback
+import androidx.core.telecom.CallControlScope
+import androidx.core.telecom.CallEndpointCompat
+import androidx.core.telecom.CallException
+import androidx.core.telecom.internal.utils.EndpointUtils
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+@RequiresApi(VERSION_CODES.O)
+internal class CallSessionLegacy(
+ private val id: ParcelUuid,
+ private val callChannels: CallChannels,
+ private val coroutineContext: CoroutineContext
+) : android.telecom.Connection() {
+ // instance vars
+ private val TAG: String = CallSessionLegacy::class.java.simpleName
+ private var mClientInterface: CallControlCallback? = null
+ private var mCachedBluetoothDevices: ArrayList<BluetoothDevice> = ArrayList()
+
+ companion object {
+ // CallStates. All these states mirror the values in the platform.
+ const val STATE_INITIALIZING = 0
+ const val STATE_NEW = 1
+ const val STATE_RINGING = 2
+ const val STATE_DIALING = 3
+ const val STATE_ACTIVE = 4
+ const val STATE_HOLDING = 5
+ const val STATE_DISCONNECTED = 6
+ }
+
+ fun setCallControlCallback(callControlCallback: CallControlCallback) {
+ mClientInterface = callControlCallback
+ }
+
+ fun hasClientSetCallbacks(): Boolean {
+ return mClientInterface != null
+ }
+
+ /**
+ * =========================================================================================
+ * Call State Updates
+ * =========================================================================================
+ */
+ override fun onStateChanged(state: Int) {
+ Log.v(TAG, "onStateChanged: state=${platformCallStateToString(state)}")
+ }
+
+ private fun platformCallStateToString(state: Int): String {
+ return when (state) {
+ STATE_INITIALIZING -> "INITIALIZING"
+ STATE_NEW -> "NEW"
+ STATE_DIALING -> "DIALING"
+ STATE_RINGING -> "RINGING"
+ STATE_ACTIVE -> "ACTIVE"
+ STATE_HOLDING -> "HOLDING"
+ STATE_DISCONNECTED -> "DISCONNECTED"
+ else -> "UNKNOWN"
+ }
+ }
+
+ /**
+ * =========================================================================================
+ * Audio Updates
+ * =========================================================================================
+ */
+ override fun onCallAudioStateChanged(state: CallAudioState) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.P) {
+ Api28PlusImpl.refreshBluetoothDeviceCache(mCachedBluetoothDevices, state)
+ }
+ callChannels.currentEndpointChannel.trySend(
+ EndpointUtils.toCallEndpointCompat(state)
+ ).getOrThrow()
+
+ callChannels.availableEndpointChannel.trySend(
+ EndpointUtils.toCallEndpointsCompat(state)
+ ).getOrThrow()
+
+ callChannels.isMutedChannel.trySend(state.isMuted).getOrThrow()
+ }
+
+ /**
+ * =========================================================================================
+ * CallControl
+ * =========================================================================================
+ */
+
+ fun getCallId(): ParcelUuid {
+ return id
+ }
+
+ fun answer(videoState: Int): Boolean {
+ setVideoState(videoState)
+ setActive()
+ return true
+ }
+
+ fun setConnectionActive(): Boolean {
+ setActive()
+ return true
+ }
+
+ fun setConnectionInactive(): Boolean {
+ setOnHold()
+ return true
+ }
+
+ fun setConnectionDisconnect(cause: DisconnectCause): Boolean {
+ setDisconnected(cause)
+ destroy()
+ return true
+ }
+
+ @Suppress("deprecation")
+ fun requestEndpointChange(callEndpoint: CallEndpointCompat): Boolean {
+ return if (Build.VERSION.SDK_INT < VERSION_CODES.P) {
+ Api26PlusImpl.setAudio(callEndpoint, this)
+ true
+ } else {
+ Api28PlusImpl.setAudio(callEndpoint, this, mCachedBluetoothDevices)
+ }
+ }
+
+ @Suppress("deprecation")
+ @RequiresApi(VERSION_CODES.O)
+ private object Api26PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun setAudio(callEndpoint: CallEndpointCompat, connection: CallSessionLegacy) {
+ connection.setAudioRoute(EndpointUtils.mapTypeToRoute(callEndpoint.type))
+ }
+ }
+
+ @Suppress("deprecation")
+ @RequiresApi(VERSION_CODES.P)
+ private object Api28PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun setAudio(
+ callEndpoint: CallEndpointCompat,
+ connection: CallSessionLegacy,
+ btCache: ArrayList<BluetoothDevice>
+ ): Boolean {
+ if (callEndpoint.type == CallEndpointCompat.TYPE_BLUETOOTH) {
+ val btDevice = getBluetoothDeviceFromEndpoint(btCache, callEndpoint)
+ if (btDevice != null) {
+ connection.requestBluetoothAudio(btDevice)
+ return true
+ }
+ return false
+ } else {
+ connection.setAudioRoute(EndpointUtils.mapTypeToRoute(callEndpoint.type))
+ return true
+ }
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun refreshBluetoothDeviceCache(
+ btCacheList: ArrayList<BluetoothDevice>,
+ state: CallAudioState
+ ) {
+ btCacheList.clear()
+ btCacheList.addAll(state.supportedBluetoothDevices)
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun getBluetoothDeviceFromEndpoint(
+ btCacheList: ArrayList<BluetoothDevice>,
+ endpoint: CallEndpointCompat
+ ): BluetoothDevice? {
+ for (btDevice in btCacheList) {
+ if (bluetoothDeviceMatchesEndpoint(btDevice, endpoint)) {
+ return btDevice
+ }
+ }
+ return null
+ }
+
+ fun bluetoothDeviceMatchesEndpoint(btDevice: BluetoothDevice, endpoint: CallEndpointCompat):
+ Boolean {
+ return (btDevice.address?.equals(endpoint.mMackAddress) ?: false)
+ }
+ }
+
+ /**
+ * =========================================================================================
+ * CallControlCallbacks
+ * =========================================================================================
+ */
+ override fun onAnswer(videoState: Int) {
+ CoroutineScope(coroutineContext).launch {
+ val clientCanAnswer = mClientInterface!!.onSetActive()
+ if (clientCanAnswer) {
+ setActive()
+ setVideoState(videoState)
+ }
+ }
+ }
+
+ override fun onUnhold() {
+ CoroutineScope(coroutineContext).launch {
+ val clientCanUnhold = mClientInterface!!.onSetActive()
+ if (clientCanUnhold) {
+ setActive()
+ }
+ }
+ }
+
+ override fun onHold() {
+ CoroutineScope(coroutineContext).launch {
+ val clientCanHold = mClientInterface!!.onSetInactive()
+ if (clientCanHold) {
+ setOnHold()
+ }
+ }
+ }
+
+ override fun onDisconnect() {
+ CoroutineScope(coroutineContext).launch {
+ mClientInterface!!.onDisconnect(
+ DisconnectCause(DisconnectCause.LOCAL)
+ )
+ setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
+ }
+ }
+
+ /**
+ * =========================================================================================
+ * Simple implementation of [CallControlScope] with a [CallSessionLegacy] as the session.
+ * =========================================================================================
+ */
+ class CallControlScopeImpl(
+ private val session: CallSessionLegacy,
+ callChannels: CallChannels
+ ) : CallControlScope {
+ // handle actionable/handshake events that originate in the platform
+ // and require a response from the client
+ override fun setCallback(callControlCallback: CallControlCallback) {
+ session.setCallControlCallback(callControlCallback)
+ }
+
+ // handle requests that originate from the client and propagate into platform
+ // return the platforms response which indicates success of the request.
+ override fun getCallId(): ParcelUuid {
+ verifySessionCallbacks()
+ return session.getCallId()
+ }
+
+ override suspend fun setActive(): Boolean {
+ verifySessionCallbacks()
+ return session.setConnectionActive()
+ }
+
+ override suspend fun setInactive(): Boolean {
+ verifySessionCallbacks()
+ return session.setConnectionInactive()
+ }
+
+ override suspend fun answer(callType: Int): Boolean {
+ verifySessionCallbacks()
+ return session.answer(callType)
+ }
+
+ override suspend fun disconnect(disconnectCause: DisconnectCause): Boolean {
+ verifySessionCallbacks()
+ return session.setConnectionDisconnect(disconnectCause)
+ }
+
+ override suspend fun requestEndpointChange(endpoint: CallEndpointCompat): Boolean {
+ verifySessionCallbacks()
+ return session.requestEndpointChange(endpoint)
+ }
+
+ // Send these events out to the client to collect
+ override val currentCallEndpoint: Flow<CallEndpointCompat> =
+ callChannels.currentEndpointChannel.receiveAsFlow()
+
+ override val availableEndpoints: Flow<List<CallEndpointCompat>> =
+ callChannels.availableEndpointChannel.receiveAsFlow()
+
+ override val isMuted: Flow<Boolean> =
+ callChannels.isMutedChannel.receiveAsFlow()
+
+ private fun verifySessionCallbacks() {
+ if (!session.hasClientSetCallbacks()) {
+ throw CallException(CallException.ERROR_CALLBACKS_CODE)
+ }
+ }
+ }
+}
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/JetpackConnectionService.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/JetpackConnectionService.kt
new file mode 100644
index 0000000..d219910
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/JetpackConnectionService.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.core.telecom.internal
+
+import android.os.Build
+import android.os.ParcelUuid
+import android.telecom.Connection
+import android.telecom.ConnectionRequest
+import android.telecom.ConnectionService
+import android.telecom.PhoneAccountHandle
+import android.telecom.TelecomManager
+import android.telecom.VideoProfile
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresPermission
+import androidx.core.telecom.CallAttributesCompat
+import androidx.core.telecom.CallsManager
+import androidx.core.telecom.internal.utils.Utils
+import java.util.UUID
+import java.util.concurrent.CancellationException
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+@RequiresApi(api = Build.VERSION_CODES.O)
+internal class JetpackConnectionService : ConnectionService() {
+ val TAG: String = JetpackConnectionService::class.java.simpleName.toString()
+
+ /**
+ * Wrap all the objects that are associated with a new CallSession request into a class
+ */
+ data class PendingConnectionRequest(
+ val callAttributes: CallAttributesCompat,
+ val callChannel: CallChannels,
+ val coroutineContext: CoroutineContext,
+ val completableDeferred: CompletableDeferred<CallSessionLegacy>?
+ )
+
+ companion object {
+ const val CONNECTION_CREATION_TIMEOUT: Long = 5000 // time in milli-seconds
+ var mPendingConnectionRequests: ArrayList<PendingConnectionRequest> = ArrayList()
+ }
+
+ /**
+ * Request the Platform create a new Connection with the properties given by [CallAttributesCompat].
+ * This request will have a timeout of [CONNECTION_CREATION_TIMEOUT] and be removed when the
+ * result is completed.
+ */
+ @RequiresPermission(value = "android.permission.MANAGE_OWN_CALLS")
+ fun createConnectionRequest(
+ telecomManager: TelecomManager,
+ pendingConnectionRequest: PendingConnectionRequest,
+ ) {
+ // add request to list
+ mPendingConnectionRequests.add(pendingConnectionRequest)
+
+ val extras = Utils.getBundleWithPhoneAccountHandle(
+ pendingConnectionRequest.callAttributes,
+ pendingConnectionRequest.callAttributes.mHandle!!
+ )
+
+ // Call into the platform to start call
+ if (pendingConnectionRequest.callAttributes.isOutgoingCall()) {
+ telecomManager.placeCall(
+ pendingConnectionRequest.callAttributes.address,
+ extras
+ )
+ } else {
+ telecomManager.addNewIncomingCall(
+ pendingConnectionRequest.callAttributes.mHandle,
+ extras
+ )
+ }
+
+ // create a job that times out if the connection cannot be created in x amount of time
+ CoroutineScope(pendingConnectionRequest.coroutineContext).launch {
+ delay(CONNECTION_CREATION_TIMEOUT)
+ if (!pendingConnectionRequest.completableDeferred!!.isCompleted) {
+ Log.i(
+ TAG, "The request to create a connection timed out. Cancelling the" +
+ "request to add the call to Telecom."
+ )
+ mPendingConnectionRequests.remove(pendingConnectionRequest)
+ pendingConnectionRequest.completableDeferred.cancel(
+ CancellationException(CallsManager.CALL_CREATION_FAILURE_MSG)
+ )
+ }
+ }
+ }
+
+ /**
+ * Outgoing Connections
+ */
+ override fun onCreateOutgoingConnection(
+ connectionManagerAccount: PhoneAccountHandle,
+ request: ConnectionRequest
+ ): Connection? {
+ return createSelfManagedConnection(
+ request,
+ CallAttributesCompat.DIRECTION_OUTGOING
+ )
+ }
+
+ override fun onCreateOutgoingConnectionFailed(
+ connectionManagerPhoneAccount: PhoneAccountHandle,
+ request: ConnectionRequest
+ ) {
+ val pendingRequest: PendingConnectionRequest? =
+ findTargetPendingConnectionRequest(
+ request,
+ CallAttributesCompat.DIRECTION_OUTGOING
+ )
+ pendingRequest?.completableDeferred?.cancel()
+
+ mPendingConnectionRequests.remove(pendingRequest)
+ }
+
+ /**
+ * Incoming Connections
+ */
+ override fun onCreateIncomingConnection(
+ connectionManagerPhoneAccount: PhoneAccountHandle,
+ request: ConnectionRequest
+ ): Connection? {
+ return createSelfManagedConnection(
+ request,
+ CallAttributesCompat.DIRECTION_INCOMING
+ )
+ }
+
+ override fun onCreateIncomingConnectionFailed(
+ connectionManagerPhoneAccount: PhoneAccountHandle,
+ request: ConnectionRequest
+ ) {
+ val pendingRequest: PendingConnectionRequest? =
+ findTargetPendingConnectionRequest(
+ request,
+ CallAttributesCompat.DIRECTION_INCOMING
+ )
+ pendingRequest?.completableDeferred?.cancel()
+ mPendingConnectionRequests.remove(pendingRequest)
+ }
+
+ internal fun createSelfManagedConnection(request: ConnectionRequest, direction: Int):
+ Connection? {
+ val targetRequest: PendingConnectionRequest =
+ findTargetPendingConnectionRequest(request, direction) ?: return null
+
+ val jetpackConnection = CallSessionLegacy(
+ ParcelUuid.fromString(UUID.randomUUID().toString()),
+ targetRequest.callChannel,
+ targetRequest.coroutineContext
+ )
+
+ // set display name
+ jetpackConnection.setCallerDisplayName(
+ targetRequest.callAttributes.displayName.toString(),
+ TelecomManager.PRESENTATION_ALLOWED
+ )
+
+ // set address
+ jetpackConnection.setAddress(
+ targetRequest.callAttributes.address,
+ TelecomManager.PRESENTATION_ALLOWED
+ )
+
+ // set the call state for the given direction
+ if (direction == CallAttributesCompat.DIRECTION_OUTGOING) {
+ jetpackConnection.setDialing()
+ } else {
+ jetpackConnection.setRinging()
+ }
+
+ // set the callType
+ if (targetRequest.callAttributes.callType
+ == CallAttributesCompat.CALL_TYPE_VIDEO_CALL
+ ) {
+ jetpackConnection.setVideoState(VideoProfile.STATE_BIDIRECTIONAL)
+ } else {
+ jetpackConnection.setVideoState(VideoProfile.STATE_AUDIO_ONLY)
+ }
+
+ // set the call capabilities
+ if (targetRequest.callAttributes.hasSupportsSetInactiveCapability()) {
+ jetpackConnection.setConnectionCapabilities(
+ Connection.CAPABILITY_HOLD or Connection.CAPABILITY_SUPPORT_HOLD
+ )
+ }
+
+ targetRequest.completableDeferred?.complete(jetpackConnection)
+ mPendingConnectionRequests.remove(targetRequest)
+
+ return jetpackConnection
+ }
+
+ /**
+ * Helper methods
+ */
+ private fun findTargetPendingConnectionRequest(
+ request: ConnectionRequest,
+ direction: Int
+ ): PendingConnectionRequest? {
+ for (pendingConnectionRequest in mPendingConnectionRequests) {
+ if (isSameAddress(pendingConnectionRequest.callAttributes, request) &&
+ isSameDirection(pendingConnectionRequest.callAttributes, direction) &&
+ isSameHandle(pendingConnectionRequest.callAttributes.mHandle, request)
+ ) {
+ return pendingConnectionRequest
+ }
+ }
+ return null
+ }
+
+ private fun isSameDirection(callAttributes: CallAttributesCompat, direction: Int): Boolean {
+ return (callAttributes.direction == direction)
+ }
+
+ private fun isSameAddress(
+ callAttributes: CallAttributesCompat,
+ request: ConnectionRequest
+ ): Boolean {
+ return request.address?.equals(callAttributes.address) ?: false
+ }
+
+ private fun isSameHandle(handle: PhoneAccountHandle?, request: ConnectionRequest): Boolean {
+ return request.accountHandle?.equals(handle) ?: false
+ }
+}
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/BuildVersionAdapter.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/BuildVersionAdapter.kt
new file mode 100644
index 0000000..9c40d14
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/BuildVersionAdapter.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal.utils
+
+internal interface BuildVersionAdapter {
+ fun hasPlatformV2Apis(): Boolean
+ fun hasInvalidBuildVersion(): Boolean
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt
new file mode 100644
index 0000000..5148edd
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal.utils
+
+import android.net.Uri
+import android.telecom.PhoneAccountHandle
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallAttributesCompat
+
+@RequiresApi(34)
+internal class CallAttributesUtils {
+ internal object Api34PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun toTelecomCallAttributes(
+ phoneAccountHandle: PhoneAccountHandle,
+ direction: Int,
+ displayName: CharSequence,
+ address: Uri,
+ callType: Int,
+ callCapabilities: Int
+ ): android.telecom.CallAttributes {
+ return android.telecom.CallAttributes.Builder(
+ phoneAccountHandle,
+ direction,
+ displayName,
+ address
+ )
+ .setCallType(remapCallType(callType))
+ .setCallCapabilities(remapCapabilities(callCapabilities))
+ .build()
+ }
+
+ private fun remapCallType(callType: Int): Int {
+ return if (callType == CallAttributesCompat.CALL_TYPE_AUDIO_CALL) {
+ android.telecom.CallAttributes.AUDIO_CALL
+ } else {
+ android.telecom.CallAttributes.VIDEO_CALL
+ }
+ }
+
+ private fun remapCapabilities(callCapabilities: Int): Int {
+ var bitMap: Int = 0
+ if (hasSupportsSetInactiveCapability(callCapabilities)) {
+ bitMap = bitMap or android.telecom.CallAttributes.SUPPORTS_SET_INACTIVE
+ }
+ if (hasStreamCapability(callCapabilities)) {
+ bitMap = bitMap or android.telecom.CallAttributes.SUPPORTS_STREAM
+ }
+ if (hasTransferCapability(callCapabilities)) {
+ bitMap = bitMap or android.telecom.CallAttributes.SUPPORTS_TRANSFER
+ }
+ return bitMap
+ }
+
+ private fun hasSupportsSetInactiveCapability(callCapabilities: Int): Boolean {
+ return Utils.hasCapability(CallAttributesCompat.SUPPORTS_SET_INACTIVE, callCapabilities)
+ }
+
+ private fun hasStreamCapability(callCapabilities: Int): Boolean {
+ return Utils.hasCapability(CallAttributesCompat.SUPPORTS_STREAM, callCapabilities)
+ }
+
+ private fun hasTransferCapability(callCapabilities: Int): Boolean {
+ return Utils.hasCapability(CallAttributesCompat.SUPPORTS_TRANSFER, callCapabilities)
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt
new file mode 100644
index 0000000..7075dc5
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal.utils
+
+import android.bluetooth.BluetoothDevice
+import android.os.Build
+import android.os.Build.VERSION.SDK_INT
+import android.os.Build.VERSION_CODES.P
+import android.telecom.CallAudioState
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallEndpointCompat
+
+@RequiresApi(Build.VERSION_CODES.O)
+internal class EndpointUtils {
+
+ companion object {
+ fun toCallEndpointCompat(state: CallAudioState): CallEndpointCompat {
+ val type: Int = mapRouteToType(state.route)
+ return if (type == CallEndpointCompat.TYPE_BLUETOOTH && SDK_INT >= P) {
+ BluetoothApi28PlusImpl.getCallEndpointFromAudioState(state)
+ } else {
+ CallEndpointCompat(endpointTypeToString(type), type)
+ }
+ }
+
+ fun toCallEndpointsCompat(state: CallAudioState): List<CallEndpointCompat> {
+ val endpoints: ArrayList<CallEndpointCompat> = ArrayList()
+ val bitMask = state.supportedRouteMask
+ if (hasEarpieceType(bitMask)) {
+ endpoints.add(
+ CallEndpointCompat(
+ endpointTypeToString(CallEndpointCompat.TYPE_EARPIECE),
+ CallEndpointCompat.TYPE_EARPIECE
+ )
+ )
+ }
+ if (hasBluetoothType(bitMask)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ endpoints.addAll(BluetoothApi28PlusImpl.getBluetoothEndpoints(state))
+ } else {
+ endpoints.add(
+ CallEndpointCompat(
+ endpointTypeToString(CallEndpointCompat.TYPE_BLUETOOTH),
+ CallEndpointCompat.TYPE_BLUETOOTH
+ )
+ )
+ }
+ }
+ if (hasWiredHeadsetType(bitMask)) {
+ endpoints.add(
+ CallEndpointCompat(
+ endpointTypeToString(CallEndpointCompat.TYPE_WIRED_HEADSET),
+ CallEndpointCompat.TYPE_WIRED_HEADSET
+ )
+ )
+ }
+ if (hasSpeakerType(bitMask)) {
+ endpoints.add(
+ CallEndpointCompat(
+ endpointTypeToString(CallEndpointCompat.TYPE_SPEAKER),
+ CallEndpointCompat.TYPE_SPEAKER
+ )
+ )
+ }
+ if (hasStreamingType(bitMask)) {
+ endpoints.add(
+ CallEndpointCompat(
+ endpointTypeToString(CallEndpointCompat.TYPE_STREAMING),
+ CallEndpointCompat.TYPE_STREAMING
+ )
+ )
+ }
+ return endpoints
+ }
+
+ private fun hasEarpieceType(bitMap: Int): Boolean {
+ return (bitMap.and(CallAudioState.ROUTE_EARPIECE)) == CallAudioState.ROUTE_EARPIECE
+ }
+
+ fun hasBluetoothType(bitMap: Int): Boolean {
+ return (bitMap.and(CallAudioState.ROUTE_BLUETOOTH)) == CallAudioState.ROUTE_BLUETOOTH
+ }
+
+ fun hasWiredHeadsetType(bitMap: Int): Boolean {
+ return (bitMap.and(CallAudioState.ROUTE_WIRED_HEADSET)
+ ) == CallAudioState.ROUTE_WIRED_HEADSET
+ }
+
+ fun hasSpeakerType(bitMap: Int): Boolean {
+ return (bitMap.and(CallAudioState.ROUTE_SPEAKER)) == CallAudioState.ROUTE_SPEAKER
+ }
+
+ fun hasStreamingType(bitMap: Int): Boolean {
+ return (bitMap.and(CallAudioState.ROUTE_STREAMING)) == CallAudioState.ROUTE_STREAMING
+ }
+
+ fun mapRouteToType(route: Int): @CallEndpointCompat.Companion.EndpointType Int {
+ return when (route) {
+ CallAudioState.ROUTE_EARPIECE -> CallEndpointCompat.TYPE_EARPIECE
+ CallAudioState.ROUTE_BLUETOOTH -> CallEndpointCompat.TYPE_BLUETOOTH
+ CallAudioState.ROUTE_WIRED_HEADSET -> CallEndpointCompat.TYPE_WIRED_HEADSET
+ CallAudioState.ROUTE_SPEAKER -> CallEndpointCompat.TYPE_SPEAKER
+ CallAudioState.ROUTE_STREAMING -> CallEndpointCompat.TYPE_STREAMING
+ else -> CallEndpointCompat.TYPE_UNKNOWN
+ }
+ }
+
+ fun mapTypeToRoute(route: Int): Int {
+ return when (route) {
+ CallEndpointCompat.TYPE_EARPIECE -> CallAudioState.ROUTE_EARPIECE
+ CallEndpointCompat.TYPE_BLUETOOTH -> CallAudioState.ROUTE_BLUETOOTH
+ CallEndpointCompat.TYPE_WIRED_HEADSET -> CallAudioState.ROUTE_WIRED_HEADSET
+ CallEndpointCompat.TYPE_SPEAKER -> CallAudioState.ROUTE_SPEAKER
+ CallEndpointCompat.TYPE_STREAMING -> CallAudioState.ROUTE_STREAMING
+ else -> CallAudioState.ROUTE_EARPIECE
+ }
+ }
+
+ fun endpointTypeToString(endpointType: Int): String {
+ return when (endpointType) {
+ CallEndpointCompat.TYPE_EARPIECE -> "EARPIECE"
+ CallEndpointCompat.TYPE_BLUETOOTH -> "BLUETOOTH"
+ CallEndpointCompat.TYPE_WIRED_HEADSET -> "WIRED_HEADSET"
+ CallEndpointCompat.TYPE_SPEAKER -> "SPEAKER"
+ CallEndpointCompat.TYPE_STREAMING -> "EXTERNAL"
+ else -> "UNKNOWN ($endpointType)"
+ }
+ }
+ }
+
+ @RequiresApi(34)
+ object Api34PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun toCallEndpointCompat(endpoint: android.telecom.CallEndpoint):
+ CallEndpointCompat {
+ return CallEndpointCompat(
+ endpoint.endpointName,
+ endpoint.endpointType,
+ endpoint.identifier
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun toCallEndpointsCompat(endpoints: List<android.telecom.CallEndpoint>):
+ List<CallEndpointCompat> {
+ val res = ArrayList<CallEndpointCompat>()
+ for (e in endpoints) {
+ res.add(CallEndpointCompat(e.endpointName, e.endpointType, e.identifier))
+ }
+ return res
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun toCallEndpoint(e: CallEndpointCompat): android.telecom.CallEndpoint {
+ return android.telecom.CallEndpoint(e.name, e.type, e.identifier)
+ }
+ }
+
+ @RequiresApi(28)
+ object BluetoothApi28PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun getBluetoothEndpoints(state: CallAudioState):
+ ArrayList<CallEndpointCompat> {
+ val endpoints: ArrayList<CallEndpointCompat> = ArrayList()
+ val supportedBluetoothDevices = state.supportedBluetoothDevices
+ for (bluetoothDevice in supportedBluetoothDevices) {
+ endpoints.add(getCallEndpointFromBluetoothDevice(bluetoothDevice))
+ }
+ return endpoints
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun getCallEndpointFromBluetoothDevice(btDevice: BluetoothDevice?): CallEndpointCompat {
+ var endpointName: String = "Bluetooth Device"
+ var endpointIdentity: String = "Unknown Address"
+ if (btDevice != null) {
+ endpointIdentity = btDevice.address
+ try {
+ endpointName = btDevice.name
+ } catch (e: SecurityException) {
+ // pass through
+ }
+ }
+ return CallEndpointCompat(
+ endpointName,
+ CallEndpointCompat.TYPE_BLUETOOTH,
+ endpointIdentity
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun getCallEndpointFromAudioState(state: CallAudioState): CallEndpointCompat {
+ return getCallEndpointFromBluetoothDevice(state.activeBluetoothDevice)
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt
new file mode 100644
index 0000000..4ab0ec2
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.telecom.internal.utils
+
+import android.os.Build.VERSION
+import android.os.Build.VERSION_CODES
+import android.os.Bundle
+import android.telecom.PhoneAccount
+import android.telecom.PhoneAccountHandle
+import android.telecom.TelecomManager
+import android.util.Log
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallAttributesCompat
+import androidx.core.telecom.CallException
+import androidx.core.telecom.CallsManager
+
+internal class Utils {
+ companion object {
+ private val TAG = Utils.Companion::class.java.simpleName
+
+ private val defaultBuildAdapter =
+ object : BuildVersionAdapter {
+ /**
+ * Helper method that determines if the device has a build that contains the Telecom V2
+ * VoIP APIs. These include [TelecomManager#addCall], android.telecom.CallControl,
+ * android.telecom.CallEventCallback but are not limited to only those classes.
+ */
+ override fun hasPlatformV2Apis(): Boolean {
+ Log.i(TAG, "hasPlatformV2Apis: " +
+ "versionSdkInt=[${VERSION.SDK_INT}]")
+ return VERSION.SDK_INT >= 34 || VERSION.CODENAME == "UpsideDownCake"
+ }
+
+ override fun hasInvalidBuildVersion(): Boolean {
+ Log.i(TAG, "hasInvalidBuildVersion: " +
+ "versionSdkInt=[${VERSION.SDK_INT}]")
+ return VERSION.SDK_INT < VERSION_CODES.O
+ }
+ }
+ private var mBuildVersion: BuildVersionAdapter = defaultBuildAdapter
+
+ internal fun setUtils(utils: BuildVersionAdapter) {
+ mBuildVersion = utils
+ }
+
+ internal fun resetUtils() {
+ mBuildVersion = defaultBuildAdapter
+ }
+
+ fun hasPlatformV2Apis(): Boolean {
+ return mBuildVersion.hasPlatformV2Apis()
+ }
+
+ fun hasInvalidBuildVersion(): Boolean {
+ return mBuildVersion.hasInvalidBuildVersion()
+ }
+
+ fun verifyBuildVersion() {
+ if (mBuildVersion.hasInvalidBuildVersion()) {
+ throw UnsupportedOperationException(CallException.ERROR_BUILD_VERSION)
+ }
+ }
+
+ fun remapJetpackCapabilitiesToPlatformCapabilities(
+ @CallsManager.Companion.Capability clientBitmapSelection: Int
+ ): Int {
+ var remappedCapabilities = 0
+
+ if (hasJetpackVideoCallingCapability(clientBitmapSelection)) {
+ remappedCapabilities =
+ PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING or
+ remappedCapabilities
+ }
+
+ if (hasJetpackSteamingCapability(clientBitmapSelection)) {
+ remappedCapabilities =
+ PhoneAccount.CAPABILITY_SUPPORTS_CALL_STREAMING or
+ remappedCapabilities
+ }
+ return remappedCapabilities
+ }
+
+ fun hasCapability(targetCapability: Int, bitMap: Int): Boolean {
+ return (bitMap.and(targetCapability)) == targetCapability
+ }
+
+ private fun hasJetpackVideoCallingCapability(bitMap: Int): Boolean {
+ return hasCapability(CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING, bitMap)
+ }
+
+ private fun hasJetpackSteamingCapability(bitMap: Int): Boolean {
+ return hasCapability(CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING, bitMap)
+ }
+
+ fun getBundleWithPhoneAccountHandle(
+ callAttributes: CallAttributesCompat,
+ handle: PhoneAccountHandle
+ ): Bundle {
+ return if (VERSION.SDK_INT >= VERSION_CODES.M) {
+ Api23PlusImpl.createExtras(callAttributes, handle)
+ } else {
+ Bundle()
+ }
+ }
+
+ @RequiresApi(VERSION_CODES.M)
+ private object Api23PlusImpl {
+ @JvmStatic
+ @DoNotInline
+ fun createExtras(
+ callAttributes: CallAttributesCompat,
+ handle: PhoneAccountHandle
+ ): Bundle {
+ val extras = Bundle()
+ extras.putParcelable(
+ TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ handle
+ )
+ if (!callAttributes.isOutgoingCall()) {
+ extras.putParcelable(
+ TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
+ callAttributes.address
+ )
+ }
+ return extras
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/core/api/1.11.0-beta01.txt b/core/core/api/1.11.0-beta01.txt
index 128f0c4..fa087dc 100644
--- a/core/core/api/1.11.0-beta01.txt
+++ b/core/core/api/1.11.0-beta01.txt
@@ -1842,19 +1842,12 @@
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
- method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastSv2();
- method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastT();
- method @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastU();
- method @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.R) public static final int R_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.S) public static final int S_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.TIRAMISU) public static final int T_EXTENSION_INT;
}
- @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface BuildCompat.PrereleaseSdkCheck {
- }
-
public final class BundleCompat {
method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 128f0c4..ca9c5ca 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -156,6 +156,15 @@
field public static final int TOTAL_INDEX = 0; // 0x0
}
+ public final class GrammaticalInflectionManagerCompat {
+ method @AnyThread public static int getApplicationGrammaticalGender(android.content.Context);
+ method @AnyThread public static void setRequestedApplicationGrammaticalGender(android.content.Context, int);
+ field public static final int GRAMMATICAL_GENDER_FEMININE = 2; // 0x2
+ field public static final int GRAMMATICAL_GENDER_MASCULINE = 3; // 0x3
+ field public static final int GRAMMATICAL_GENDER_NEUTRAL = 1; // 0x1
+ field public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0; // 0x0
+ }
+
@Deprecated public abstract class JobIntentService extends android.app.Service {
ctor @Deprecated public JobIntentService();
method @Deprecated public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
@@ -794,6 +803,7 @@
public final class NotificationManagerCompat {
method public boolean areNotificationsEnabled();
+ method public boolean canUseFullScreenIntent();
method public void cancel(int);
method public void cancel(String?, int);
method public void cancelAll();
@@ -954,6 +964,7 @@
}
public final class ServiceCompat {
+ method public static void startForeground(android.app.Service, int, android.app.Notification, int);
method public static void stopForeground(android.app.Service, int);
field public static final int START_STICKY = 1; // 0x1
field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
@@ -1107,7 +1118,6 @@
ctor protected FileProvider(@XmlRes int);
method public int delete(android.net.Uri, String?, String![]?);
method public String? getType(android.net.Uri);
- method public String? getTypeAnonymous(android.net.Uri);
method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues);
@@ -2057,6 +2067,26 @@
}
+package androidx.core.service.quicksettings {
+
+ public class PendingIntentActivityWrapper {
+ ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+ ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, boolean);
+ method public android.content.Context getContext();
+ method public int getFlags();
+ method public android.content.Intent getIntent();
+ method public android.os.Bundle getOptions();
+ method public android.app.PendingIntent? getPendingIntent();
+ method public int getRequestCode();
+ method public boolean isMutable();
+ }
+
+ public class TileServiceCompat {
+ method public static void startActivityAndCollapse(android.service.quicksettings.TileService, androidx.core.service.quicksettings.PendingIntentActivityWrapper);
+ }
+
+}
+
package androidx.core.telephony {
@RequiresApi(22) public class SubscriptionManagerCompat {
@@ -2205,6 +2235,66 @@
method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
}
+ @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public final class LocalePreferences {
+ method public static String getCalendarType();
+ method public static String getCalendarType(boolean);
+ method public static String getCalendarType(java.util.Locale);
+ method public static String getCalendarType(java.util.Locale, boolean);
+ method public static String getFirstDayOfWeek();
+ method public static String getFirstDayOfWeek(boolean);
+ method public static String getFirstDayOfWeek(java.util.Locale);
+ method public static String getFirstDayOfWeek(java.util.Locale, boolean);
+ method public static String getHourCycle();
+ method public static String getHourCycle(boolean);
+ method public static String getHourCycle(java.util.Locale);
+ method public static String getHourCycle(java.util.Locale, boolean);
+ method public static String getTemperatureUnit();
+ method public static String getTemperatureUnit(boolean);
+ method public static String getTemperatureUnit(java.util.Locale);
+ method public static String getTemperatureUnit(java.util.Locale, boolean);
+ }
+
+ public static class LocalePreferences.CalendarType {
+ field public static final String CHINESE = "chinese";
+ field public static final String DANGI = "dangi";
+ field public static final String DEFAULT = "";
+ field public static final String GREGORIAN = "gregorian";
+ field public static final String HEBREW = "hebrew";
+ field public static final String INDIAN = "indian";
+ field public static final String ISLAMIC = "islamic";
+ field public static final String ISLAMIC_CIVIL = "islamic-civil";
+ field public static final String ISLAMIC_RGSA = "islamic-rgsa";
+ field public static final String ISLAMIC_TBLA = "islamic-tbla";
+ field public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+ field public static final String PERSIAN = "persian";
+ }
+
+ public static class LocalePreferences.FirstDayOfWeek {
+ field public static final String DEFAULT = "";
+ field public static final String FRIDAY = "fri";
+ field public static final String MONDAY = "mon";
+ field public static final String SATURDAY = "sat";
+ field public static final String SUNDAY = "sun";
+ field public static final String THURSDAY = "thu";
+ field public static final String TUESDAY = "tue";
+ field public static final String WEDNESDAY = "wed";
+ }
+
+ public static class LocalePreferences.HourCycle {
+ field public static final String DEFAULT = "";
+ field public static final String H11 = "h11";
+ field public static final String H12 = "h12";
+ field public static final String H23 = "h23";
+ field public static final String H24 = "h24";
+ }
+
+ public static class LocalePreferences.TemperatureUnit {
+ field public static final String CELSIUS = "celsius";
+ field public static final String DEFAULT = "";
+ field public static final String FAHRENHEIT = "fahrenhe";
+ field public static final String KELVIN = "kelvin";
+ }
+
}
package androidx.core.util {
@@ -2224,6 +2314,10 @@
method public void accept(T!);
}
+ @java.lang.FunctionalInterface public interface Function<T, R> {
+ method public R! apply(T!);
+ }
+
public class ObjectsCompat {
method public static boolean equals(Object?, Object?);
method public static int hash(java.lang.Object!...);
@@ -2286,6 +2380,14 @@
method public T! get();
}
+ public class TypedValueCompat {
+ method public static float deriveDimension(int, float, android.util.DisplayMetrics);
+ method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static float pxToDp(float, android.util.DisplayMetrics);
+ method public static float pxToSp(float, android.util.DisplayMetrics);
+ method public static float spToPx(float, android.util.DisplayMetrics);
+ }
+
}
package androidx.core.view {
@@ -2774,9 +2876,12 @@
method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
}
- @Deprecated public final class VelocityTrackerCompat {
+ public final class VelocityTrackerCompat {
+ method public static float getAxisVelocity(android.view.VelocityTracker, int);
+ method public static float getAxisVelocity(android.view.VelocityTracker, int, int);
method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+ method public static boolean isAxisSupported(android.view.VelocityTracker, int);
}
public class ViewCompat {
@@ -3005,6 +3110,8 @@
public final class ViewConfigurationCompat {
method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
method public static int getScaledHoverSlop(android.view.ViewConfiguration);
+ method public static int getScaledMaximumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+ method public static int getScaledMinimumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
@@ -3301,6 +3408,7 @@
field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+ field public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 67108864; // 0x4000000
field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
@@ -3393,6 +3501,7 @@
method public boolean isEnabled();
method public boolean isFocusable();
method public boolean isFocused();
+ method public boolean isGranularScrollingSupported();
method public boolean isHeading();
method public boolean isImportantForAccessibility();
method public boolean isLongClickable();
@@ -3437,6 +3546,7 @@
method public void setError(CharSequence!);
method public void setFocusable(boolean);
method public void setFocused(boolean);
+ method public void setGranularScrollingSupported(boolean);
method public void setHeading(boolean);
method public void setHintText(CharSequence?);
method public void setImportantForAccessibility(boolean);
@@ -3483,6 +3593,7 @@
method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+ field public static final String ACTION_ARGUMENT_DIRECTION_INT = "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -3491,6 +3602,7 @@
field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+ field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -3564,6 +3676,7 @@
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
@@ -3736,6 +3849,7 @@
}
public class AccessibilityWindowInfoCompat {
+ ctor public AccessibilityWindowInfoCompat();
method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getAnchor();
method public void getBoundsInScreen(android.graphics.Rect);
method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getChild(int);
diff --git a/core/core/api/public_plus_experimental_1.11.0-beta01.txt b/core/core/api/public_plus_experimental_1.11.0-beta01.txt
new file mode 100644
index 0000000..0c385e3
--- /dev/null
+++ b/core/core/api/public_plus_experimental_1.11.0-beta01.txt
@@ -0,0 +1,4061 @@
+// Signature format: 4.0
+package androidx.core.accessibilityservice {
+
+ public final class AccessibilityServiceInfoCompat {
+ method public static String capabilityToString(int);
+ method public static String feedbackTypeToString(int);
+ method public static String? flagToString(int);
+ method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+ method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+ field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+ field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+ field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+ field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+ field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+ field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+ field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+ field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+ field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+ field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+ field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+ }
+
+}
+
+package androidx.core.app {
+
+ public class ActivityCompat extends androidx.core.content.ContextCompat {
+ ctor protected ActivityCompat();
+ method public static void finishAffinity(android.app.Activity);
+ method public static void finishAfterTransition(android.app.Activity);
+ method public static android.net.Uri? getReferrer(android.app.Activity);
+ method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+ method public static boolean isLaunchedFromBubble(android.app.Activity);
+ method public static void postponeEnterTransition(android.app.Activity);
+ method public static void recreate(android.app.Activity);
+ method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+ method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+ method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+ method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+ method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+ method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+ method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+ method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+ method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+ method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public static void startPostponedEnterTransition(android.app.Activity);
+ }
+
+ public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+ method public void onRequestPermissionsResult(int, String![], int[]);
+ }
+
+ public static interface ActivityCompat.PermissionCompatDelegate {
+ method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+ method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+ }
+
+ public final class ActivityManagerCompat {
+ method public static boolean isLowRamDevice(android.app.ActivityManager);
+ }
+
+ public class ActivityOptionsCompat {
+ ctor protected ActivityOptionsCompat();
+ method public android.graphics.Rect? getLaunchBounds();
+ method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+ method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+ method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+ method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+ method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+ method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+ method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+ method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+ method public void requestUsageTimeReport(android.app.PendingIntent);
+ method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+ method public android.os.Bundle? toBundle();
+ method public void update(androidx.core.app.ActivityOptionsCompat);
+ field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+ field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+ }
+
+ public final class AlarmManagerCompat {
+ method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+ method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+ method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+ method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+ }
+
+ @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+ ctor public AppComponentFactory();
+ method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ }
+
+ public class AppLaunchChecker {
+ ctor @Deprecated public AppLaunchChecker();
+ method public static boolean hasStartedFromLauncher(android.content.Context);
+ method public static void onActivityCreate(android.app.Activity);
+ }
+
+ public final class AppOpsManagerCompat {
+ method public static int checkOrNoteProxyOp(android.content.Context, int, String, String);
+ method public static int noteOp(android.content.Context, String, int, String);
+ method public static int noteOpNoThrow(android.content.Context, String, int, String);
+ method public static int noteProxyOp(android.content.Context, String, String);
+ method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+ method public static String? permissionToOp(String);
+ field public static final int MODE_ALLOWED = 0; // 0x0
+ field public static final int MODE_DEFAULT = 3; // 0x3
+ field public static final int MODE_ERRORED = 2; // 0x2
+ field public static final int MODE_IGNORED = 1; // 0x1
+ }
+
+ @Deprecated public final class BundleCompat {
+ method @Deprecated public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+ method @Deprecated public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+ }
+
+ public class DialogCompat {
+ method public static android.view.View requireViewById(android.app.Dialog, int);
+ }
+
+ public class FrameMetricsAggregator {
+ ctor public FrameMetricsAggregator();
+ ctor public FrameMetricsAggregator(int);
+ method public void add(android.app.Activity);
+ method public android.util.SparseIntArray![]? getMetrics();
+ method public android.util.SparseIntArray![]? remove(android.app.Activity);
+ method public android.util.SparseIntArray![]? reset();
+ method public android.util.SparseIntArray![]? stop();
+ field public static final int ANIMATION_DURATION = 256; // 0x100
+ field public static final int ANIMATION_INDEX = 8; // 0x8
+ field public static final int COMMAND_DURATION = 32; // 0x20
+ field public static final int COMMAND_INDEX = 5; // 0x5
+ field public static final int DELAY_DURATION = 128; // 0x80
+ field public static final int DELAY_INDEX = 7; // 0x7
+ field public static final int DRAW_DURATION = 8; // 0x8
+ field public static final int DRAW_INDEX = 3; // 0x3
+ field public static final int EVERY_DURATION = 511; // 0x1ff
+ field public static final int INPUT_DURATION = 2; // 0x2
+ field public static final int INPUT_INDEX = 1; // 0x1
+ field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+ field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+ field public static final int SWAP_DURATION = 64; // 0x40
+ field public static final int SWAP_INDEX = 6; // 0x6
+ field public static final int SYNC_DURATION = 16; // 0x10
+ field public static final int SYNC_INDEX = 4; // 0x4
+ field public static final int TOTAL_DURATION = 1; // 0x1
+ field public static final int TOTAL_INDEX = 0; // 0x0
+ }
+
+ @Deprecated public abstract class JobIntentService extends android.app.Service {
+ ctor @Deprecated public JobIntentService();
+ method @Deprecated public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+ method @Deprecated public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+ method @Deprecated public boolean isStopped();
+ method @Deprecated public android.os.IBinder! onBind(android.content.Intent);
+ method @Deprecated protected abstract void onHandleWork(android.content.Intent);
+ method @Deprecated public boolean onStopCurrentWork();
+ method @Deprecated public void setInterruptIfStopped(boolean);
+ }
+
+ public final class LocaleManagerCompat {
+ method @AnyThread public static androidx.core.os.LocaleListCompat getApplicationLocales(android.content.Context);
+ method @AnyThread public static androidx.core.os.LocaleListCompat getSystemLocales(android.content.Context);
+ }
+
+ public final class MultiWindowModeChangedInfo {
+ ctor public MultiWindowModeChangedInfo(boolean);
+ ctor @RequiresApi(26) public MultiWindowModeChangedInfo(boolean, android.content.res.Configuration);
+ method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+ method public boolean isInMultiWindowMode();
+ }
+
+ public final class NavUtils {
+ method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+ method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static String? getParentActivityName(android.app.Activity);
+ method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static void navigateUpFromSameTask(android.app.Activity);
+ method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+ method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+ field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+ }
+
+ public class NotificationChannelCompat {
+ method public boolean canBubble();
+ method public boolean canBypassDnd();
+ method public boolean canShowBadge();
+ method public android.media.AudioAttributes? getAudioAttributes();
+ method public String? getConversationId();
+ method public String? getDescription();
+ method public String? getGroup();
+ method public String getId();
+ method public int getImportance();
+ method public int getLightColor();
+ method public int getLockscreenVisibility();
+ method public CharSequence? getName();
+ method public String? getParentChannelId();
+ method public android.net.Uri? getSound();
+ method public long[]? getVibrationPattern();
+ method public boolean isImportantConversation();
+ method public boolean shouldShowLights();
+ method public boolean shouldVibrate();
+ method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+ field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+ }
+
+ public static class NotificationChannelCompat.Builder {
+ ctor public NotificationChannelCompat.Builder(String, int);
+ method public androidx.core.app.NotificationChannelCompat build();
+ method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+ method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+ method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+ method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+ method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+ method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+ method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+ method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+ method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+ method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+ method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+ }
+
+ public class NotificationChannelGroupCompat {
+ method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+ method public String? getDescription();
+ method public String getId();
+ method public CharSequence? getName();
+ method public boolean isBlocked();
+ method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+ }
+
+ public static class NotificationChannelGroupCompat.Builder {
+ ctor public NotificationChannelGroupCompat.Builder(String);
+ method public androidx.core.app.NotificationChannelGroupCompat build();
+ method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+ method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+ }
+
+ public class NotificationCompat {
+ ctor @Deprecated public NotificationCompat();
+ method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+ method public static int getActionCount(android.app.Notification);
+ method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+ method public static boolean getAutoCancel(android.app.Notification);
+ method public static int getBadgeIconType(android.app.Notification);
+ method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+ method public static String? getCategory(android.app.Notification);
+ method public static String? getChannelId(android.app.Notification);
+ method public static int getColor(android.app.Notification);
+ method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+ method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+ method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+ method public static android.os.Bundle? getExtras(android.app.Notification);
+ method public static String? getGroup(android.app.Notification);
+ method public static int getGroupAlertBehavior(android.app.Notification);
+ method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+ method public static boolean getLocalOnly(android.app.Notification);
+ method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+ method public static boolean getOngoing(android.app.Notification);
+ method public static boolean getOnlyAlertOnce(android.app.Notification);
+ method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+ method public static android.app.Notification? getPublicVersion(android.app.Notification);
+ method public static CharSequence? getSettingsText(android.app.Notification);
+ method public static String? getShortcutId(android.app.Notification);
+ method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+ method public static String? getSortKey(android.app.Notification);
+ method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+ method public static long getTimeoutAfter(android.app.Notification);
+ method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+ method public static int getVisibility(android.app.Notification);
+ method public static boolean isGroupSummary(android.app.Notification);
+ method public static android.graphics.Bitmap? reduceLargeIconSize(android.content.Context, android.graphics.Bitmap?);
+ field public static final int BADGE_ICON_LARGE = 2; // 0x2
+ field public static final int BADGE_ICON_NONE = 0; // 0x0
+ field public static final int BADGE_ICON_SMALL = 1; // 0x1
+ field public static final String CATEGORY_ALARM = "alarm";
+ field public static final String CATEGORY_CALL = "call";
+ field public static final String CATEGORY_EMAIL = "email";
+ field public static final String CATEGORY_ERROR = "err";
+ field public static final String CATEGORY_EVENT = "event";
+ field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+ field public static final String CATEGORY_MESSAGE = "msg";
+ field public static final String CATEGORY_MISSED_CALL = "missed_call";
+ field public static final String CATEGORY_NAVIGATION = "navigation";
+ field public static final String CATEGORY_PROGRESS = "progress";
+ field public static final String CATEGORY_PROMO = "promo";
+ field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+ field public static final String CATEGORY_REMINDER = "reminder";
+ field public static final String CATEGORY_SERVICE = "service";
+ field public static final String CATEGORY_SOCIAL = "social";
+ field public static final String CATEGORY_STATUS = "status";
+ field public static final String CATEGORY_STOPWATCH = "stopwatch";
+ field public static final String CATEGORY_SYSTEM = "sys";
+ field public static final String CATEGORY_TRANSPORT = "transport";
+ field public static final String CATEGORY_WORKOUT = "workout";
+ field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+ field public static final int DEFAULT_ALL = -1; // 0xffffffff
+ field public static final int DEFAULT_LIGHTS = 4; // 0x4
+ field public static final int DEFAULT_SOUND = 1; // 0x1
+ field public static final int DEFAULT_VIBRATE = 2; // 0x2
+ field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+ field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+ field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+ field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+ field public static final String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo";
+ field public static final String EXTRA_CALL_PERSON = "android.callPerson";
+ field public static final String EXTRA_CALL_PERSON_COMPAT = "android.callPersonCompat";
+ field public static final String EXTRA_CALL_TYPE = "android.callType";
+ field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+ field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+ field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+ field public static final String EXTRA_COLORIZED = "android.colorized";
+ field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+ field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+ field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+ field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+ field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+ field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+ field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+ field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+ field public static final String EXTRA_INFO_TEXT = "android.infoText";
+ field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+ field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+ field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+ field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+ field public static final String EXTRA_MESSAGES = "android.messages";
+ field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+ field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+ field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+ field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+ field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+ field public static final String EXTRA_PICTURE = "android.picture";
+ field public static final String EXTRA_PICTURE_CONTENT_DESCRIPTION = "android.pictureContentDescription";
+ field public static final String EXTRA_PICTURE_ICON = "android.pictureIcon";
+ field public static final String EXTRA_PROGRESS = "android.progress";
+ field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+ field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+ field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+ field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+ field public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED = "android.showBigPictureWhenCollapsed";
+ field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+ field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+ field public static final String EXTRA_SMALL_ICON = "android.icon";
+ field public static final String EXTRA_SUB_TEXT = "android.subText";
+ field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+ field public static final String EXTRA_TEMPLATE = "android.template";
+ field public static final String EXTRA_TEXT = "android.text";
+ field public static final String EXTRA_TEXT_LINES = "android.textLines";
+ field public static final String EXTRA_TITLE = "android.title";
+ field public static final String EXTRA_TITLE_BIG = "android.title.big";
+ field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+ field public static final String EXTRA_VERIFICATION_ICON_COMPAT = "android.verificationIconCompat";
+ field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+ field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+ field public static final int FLAG_BUBBLE = 4096; // 0x1000
+ field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+ field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+ field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+ field public static final int FLAG_INSISTENT = 4; // 0x4
+ field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+ field public static final int FLAG_NO_CLEAR = 32; // 0x20
+ field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+ field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+ field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+ field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
+ field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
+ field public static final int FOREGROUND_SERVICE_IMMEDIATE = 1; // 0x1
+ field public static final int GROUP_ALERT_ALL = 0; // 0x0
+ field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+ field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+ field public static final String GROUP_KEY_SILENT = "silent";
+ field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+ field public static final int PRIORITY_DEFAULT = 0; // 0x0
+ field public static final int PRIORITY_HIGH = 1; // 0x1
+ field public static final int PRIORITY_LOW = -1; // 0xffffffff
+ field public static final int PRIORITY_MAX = 2; // 0x2
+ field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+ field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+ field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+ field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+ field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+ }
+
+ public static class NotificationCompat.Action {
+ ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+ ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+ method public android.app.PendingIntent? getActionIntent();
+ method public boolean getAllowGeneratedReplies();
+ method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+ method public android.os.Bundle getExtras();
+ method @Deprecated public int getIcon();
+ method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+ method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+ method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+ method public boolean getShowsUserInterface();
+ method public CharSequence? getTitle();
+ method public boolean isAuthenticationRequired();
+ method public boolean isContextual();
+ field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+ field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+ field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+ field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+ field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+ field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+ field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+ field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+ field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+ field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+ field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+ field public android.app.PendingIntent? actionIntent;
+ field @Deprecated public int icon;
+ field public CharSequence! title;
+ }
+
+ public static final class NotificationCompat.Action.Builder {
+ ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+ ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+ ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+ method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+ method public androidx.core.app.NotificationCompat.Action build();
+ method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+ method public android.os.Bundle getExtras();
+ method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+ method public androidx.core.app.NotificationCompat.Action.Builder setAuthenticationRequired(boolean);
+ method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+ method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+ method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+ }
+
+ public static interface NotificationCompat.Action.Extender {
+ method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+ }
+
+ @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+ }
+
+ public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+ ctor public NotificationCompat.Action.WearableExtender();
+ ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+ method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+ method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+ method @Deprecated public CharSequence? getCancelLabel();
+ method @Deprecated public CharSequence? getConfirmLabel();
+ method public boolean getHintDisplayActionInline();
+ method public boolean getHintLaunchesActivity();
+ method @Deprecated public CharSequence? getInProgressLabel();
+ method public boolean isAvailableOffline();
+ method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+ method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+ method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+ }
+
+ public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.BigPictureStyle();
+ ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+ method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+ method @RequiresApi(23) public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon?);
+ method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+ method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.drawable.Icon?);
+ method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+ method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle setContentDescription(CharSequence?);
+ method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+ method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle showBigPictureWhenCollapsed(boolean);
+ }
+
+ public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.BigTextStyle();
+ ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+ method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+ method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+ method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+ }
+
+ public static final class NotificationCompat.BubbleMetadata {
+ method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+ method public boolean getAutoExpandBubble();
+ method public android.app.PendingIntent? getDeleteIntent();
+ method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+ method @DimenRes public int getDesiredHeightResId();
+ method public androidx.core.graphics.drawable.IconCompat? getIcon();
+ method public android.app.PendingIntent? getIntent();
+ method public String? getShortcutId();
+ method public boolean isNotificationSuppressed();
+ method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+ }
+
+ public static final class NotificationCompat.BubbleMetadata.Builder {
+ ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+ ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+ ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+ method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+ }
+
+ public static class NotificationCompat.Builder {
+ ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+ ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+ ctor public NotificationCompat.Builder(android.content.Context, String);
+ method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+ method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+ method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+ method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+ method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+ method public android.app.Notification build();
+ method public androidx.core.app.NotificationCompat.Builder clearActions();
+ method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+ method public androidx.core.app.NotificationCompat.Builder clearPeople();
+ method public android.widget.RemoteViews? createBigContentView();
+ method public android.widget.RemoteViews? createContentView();
+ method public android.widget.RemoteViews? createHeadsUpContentView();
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+ method public android.os.Bundle getExtras();
+ method @Deprecated public android.app.Notification getNotification();
+ method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(int);
+ method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+ method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+ method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+ method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+ method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+ method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+ method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+ method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+ method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+ method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+ method public androidx.core.app.NotificationCompat.Builder setForegroundServiceBehavior(int);
+ method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+ method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+ method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+ method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+ method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.drawable.Icon?);
+ method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+ method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+ method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+ method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+ method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+ method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+ method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+ method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+ method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+ method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+ method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+ method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+ method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+ method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+ method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+ method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+ method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, int);
+ method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+ method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+ method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+ method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+ method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+ method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+ method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+ method public androidx.core.app.NotificationCompat.Builder setVisibility(int);
+ method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+ field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+ }
+
+ public static class NotificationCompat.CallStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.CallStyle();
+ ctor public NotificationCompat.CallStyle(androidx.core.app.NotificationCompat.Builder?);
+ method public static androidx.core.app.NotificationCompat.CallStyle forIncomingCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+ method public static androidx.core.app.NotificationCompat.CallStyle forOngoingCall(androidx.core.app.Person, android.app.PendingIntent);
+ method public static androidx.core.app.NotificationCompat.CallStyle forScreeningCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+ method public androidx.core.app.NotificationCompat.CallStyle setAnswerButtonColorHint(@ColorInt int);
+ method public androidx.core.app.NotificationCompat.CallStyle setDeclineButtonColorHint(@ColorInt int);
+ method public androidx.core.app.NotificationCompat.CallStyle setIsVideo(boolean);
+ method public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.Bitmap?);
+ method @RequiresApi(23) public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.drawable.Icon?);
+ method public androidx.core.app.NotificationCompat.CallStyle setVerificationText(CharSequence?);
+ field public static final int CALL_TYPE_INCOMING = 1; // 0x1
+ field public static final int CALL_TYPE_ONGOING = 2; // 0x2
+ field public static final int CALL_TYPE_SCREENING = 3; // 0x3
+ field public static final int CALL_TYPE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+ ctor public NotificationCompat.CarExtender();
+ ctor public NotificationCompat.CarExtender(android.app.Notification);
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ method @ColorInt public int getColor();
+ method public android.graphics.Bitmap? getLargeIcon();
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+ method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+ method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+ }
+
+ @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+ method @Deprecated public long getLatestTimestamp();
+ method @Deprecated public String![]? getMessages();
+ method @Deprecated public String? getParticipant();
+ method @Deprecated public String![]? getParticipants();
+ method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+ method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+ method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+ }
+
+ @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+ ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+ method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+ }
+
+ public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.DecoratedCustomViewStyle();
+ }
+
+ public static interface NotificationCompat.Extender {
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ }
+
+ public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.InboxStyle();
+ ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+ method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+ method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+ method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+ }
+
+ public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+ ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+ ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+ method public void addCompatExtras(android.os.Bundle);
+ method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+ method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+ method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+ method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+ method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+ method public CharSequence? getConversationTitle();
+ method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+ method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+ method public androidx.core.app.Person getUser();
+ method @Deprecated public CharSequence? getUserDisplayName();
+ method public boolean isGroupConversation();
+ method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+ method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+ field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+ }
+
+ public static final class NotificationCompat.MessagingStyle.Message {
+ ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+ ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+ method public String? getDataMimeType();
+ method public android.net.Uri? getDataUri();
+ method public android.os.Bundle getExtras();
+ method public androidx.core.app.Person? getPerson();
+ method @Deprecated public CharSequence? getSender();
+ method public CharSequence? getText();
+ method public long getTimestamp();
+ method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+ }
+
+ public abstract static class NotificationCompat.Style {
+ ctor public NotificationCompat.Style();
+ method public android.app.Notification? build();
+ method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+ }
+
+ public static final class NotificationCompat.TvExtender implements androidx.core.app.NotificationCompat.Extender {
+ ctor public NotificationCompat.TvExtender();
+ ctor public NotificationCompat.TvExtender(android.app.Notification);
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ method public String? getChannelId();
+ method public android.app.PendingIntent? getContentIntent();
+ method public android.app.PendingIntent? getDeleteIntent();
+ method public boolean isAvailableOnTv();
+ method public boolean isSuppressShowOverApps();
+ method public androidx.core.app.NotificationCompat.TvExtender setChannelId(String?);
+ method public androidx.core.app.NotificationCompat.TvExtender setContentIntent(android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.TvExtender setDeleteIntent(android.app.PendingIntent?);
+ method public androidx.core.app.NotificationCompat.TvExtender setSuppressShowOverApps(boolean);
+ }
+
+ public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+ ctor public NotificationCompat.WearableExtender();
+ ctor public NotificationCompat.WearableExtender(android.app.Notification);
+ method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+ method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+ method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+ method public androidx.core.app.NotificationCompat.WearableExtender clone();
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+ method @Deprecated public android.graphics.Bitmap? getBackground();
+ method public String? getBridgeTag();
+ method public int getContentAction();
+ method @Deprecated public int getContentIcon();
+ method @Deprecated public int getContentIconGravity();
+ method public boolean getContentIntentAvailableOffline();
+ method @Deprecated public int getCustomContentHeight();
+ method @Deprecated public int getCustomSizePreset();
+ method public String? getDismissalId();
+ method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+ method @Deprecated public int getGravity();
+ method @Deprecated public boolean getHintAmbientBigPicture();
+ method @Deprecated public boolean getHintAvoidBackgroundClipping();
+ method public boolean getHintContentIntentLaunchesActivity();
+ method @Deprecated public boolean getHintHideIcon();
+ method @Deprecated public int getHintScreenTimeout();
+ method @Deprecated public boolean getHintShowBackgroundOnly();
+ method @Deprecated public java.util.List<android.app.Notification!> getPages();
+ method public boolean getStartScrollBottom();
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+ method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+ method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+ method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+ method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+ method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+ method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+ method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+ field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+ field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+ field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+ field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+ field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+ field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+ field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+ field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+ field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+ }
+
+ public final class NotificationCompatExtras {
+ field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+ field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+ field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+ field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+ field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+ field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+ }
+
+ public abstract class NotificationCompatSideChannelService extends android.app.Service {
+ ctor public NotificationCompatSideChannelService();
+ method public abstract void cancel(String!, int, String!);
+ method public abstract void cancelAll(String!);
+ method public abstract void notify(String!, int, String!, android.app.Notification!);
+ method public android.os.IBinder! onBind(android.content.Intent!);
+ }
+
+ public final class NotificationManagerCompat {
+ method public boolean areNotificationsEnabled();
+ method public void cancel(int);
+ method public void cancel(String?, int);
+ method public void cancelAll();
+ method public void createNotificationChannel(android.app.NotificationChannel);
+ method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+ method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+ method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+ method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+ method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+ method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+ method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+ method public void deleteNotificationChannel(String);
+ method public void deleteNotificationChannelGroup(String);
+ method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+ method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+ method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
+ method public int getCurrentInterruptionFilter();
+ method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+ method public int getImportance();
+ method public android.app.NotificationChannel? getNotificationChannel(String);
+ method public android.app.NotificationChannel? getNotificationChannel(String, String);
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+ method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+ method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+ method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+ method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+ method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+ method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+ method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(int, android.app.Notification);
+ method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(String?, int, android.app.Notification);
+ method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(java.util.List<androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag!>);
+ field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+ field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+ field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+ field public static final int IMPORTANCE_HIGH = 4; // 0x4
+ field public static final int IMPORTANCE_LOW = 2; // 0x2
+ field public static final int IMPORTANCE_MAX = 5; // 0x5
+ field public static final int IMPORTANCE_MIN = 1; // 0x1
+ field public static final int IMPORTANCE_NONE = 0; // 0x0
+ field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+ field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
+ field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+ field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+ field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
+ field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+ }
+
+ public static class NotificationManagerCompat.NotificationWithIdAndTag {
+ ctor public NotificationManagerCompat.NotificationWithIdAndTag(int, android.app.Notification);
+ ctor public NotificationManagerCompat.NotificationWithIdAndTag(String?, int, android.app.Notification);
+ }
+
+ public interface OnMultiWindowModeChangedProvider {
+ method public void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+ method public void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+ }
+
+ public interface OnNewIntentProvider {
+ method public void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+ method public void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+ }
+
+ public interface OnPictureInPictureModeChangedProvider {
+ method public void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+ method public void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+ }
+
+ public final class PendingIntentCompat {
+ method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, android.os.Bundle, boolean);
+ method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, boolean);
+ method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle, boolean);
+ method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int, boolean);
+ method public static android.app.PendingIntent? getBroadcast(android.content.Context, int, android.content.Intent, int, boolean);
+ method @RequiresApi(26) public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int, boolean);
+ method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int, boolean);
+ }
+
+ public class Person {
+ method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+ method public androidx.core.graphics.drawable.IconCompat? getIcon();
+ method public String? getKey();
+ method public CharSequence? getName();
+ method public String? getUri();
+ method public boolean isBot();
+ method public boolean isImportant();
+ method public androidx.core.app.Person.Builder toBuilder();
+ method public android.os.Bundle toBundle();
+ }
+
+ public static class Person.Builder {
+ ctor public Person.Builder();
+ method public androidx.core.app.Person build();
+ method public androidx.core.app.Person.Builder setBot(boolean);
+ method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+ method public androidx.core.app.Person.Builder setImportant(boolean);
+ method public androidx.core.app.Person.Builder setKey(String?);
+ method public androidx.core.app.Person.Builder setName(CharSequence?);
+ method public androidx.core.app.Person.Builder setUri(String?);
+ }
+
+ public final class PictureInPictureModeChangedInfo {
+ ctor public PictureInPictureModeChangedInfo(boolean);
+ ctor @RequiresApi(26) public PictureInPictureModeChangedInfo(boolean, android.content.res.Configuration);
+ method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+ method public boolean isInPictureInPictureMode();
+ }
+
+ public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+ ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+ ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+ method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+ method public android.app.PendingIntent getActionIntent();
+ method public CharSequence getContentDescription();
+ method public androidx.core.graphics.drawable.IconCompat getIcon();
+ method public CharSequence getTitle();
+ method public boolean isEnabled();
+ method public void setEnabled(boolean);
+ method public void setShouldShowIcon(boolean);
+ method public boolean shouldShowIcon();
+ method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+ }
+
+ public final class RemoteInput {
+ method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String!,android.net.Uri!>);
+ method public static void addResultsToIntent(androidx.core.app.RemoteInput![], android.content.Intent, android.os.Bundle);
+ method public boolean getAllowFreeFormInput();
+ method public java.util.Set<java.lang.String!>? getAllowedDataTypes();
+ method public CharSequence![]? getChoices();
+ method public static java.util.Map<java.lang.String!,android.net.Uri!>? getDataResultsFromIntent(android.content.Intent, String);
+ method public int getEditChoicesBeforeSending();
+ method public android.os.Bundle getExtras();
+ method public CharSequence? getLabel();
+ method public String getResultKey();
+ method public static android.os.Bundle? getResultsFromIntent(android.content.Intent);
+ method public static int getResultsSource(android.content.Intent);
+ method public boolean isDataOnly();
+ method public static void setResultsSource(android.content.Intent, int);
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+ field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+ field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+ field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+ field public static final int SOURCE_CHOICE = 1; // 0x1
+ field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+ }
+
+ public static final class RemoteInput.Builder {
+ ctor public RemoteInput.Builder(String);
+ method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+ method public androidx.core.app.RemoteInput build();
+ method public android.os.Bundle getExtras();
+ method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+ method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+ method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+ method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
+ method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+ }
+
+ public final class ServiceCompat {
+ method public static void stopForeground(android.app.Service, int);
+ field public static final int START_STICKY = 1; // 0x1
+ field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+ field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+ }
+
+ public final class ShareCompat {
+ method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+ method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+ method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+ method public static String? getCallingPackage(android.app.Activity);
+ field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+ field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+ field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+ field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+ }
+
+ public static class ShareCompat.IntentBuilder {
+ ctor public ShareCompat.IntentBuilder(android.content.Context);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+ method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+ method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+ method public android.content.Intent createChooserIntent();
+ method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+ method public android.content.Intent getIntent();
+ method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+ method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+ method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+ method public void startChooser();
+ }
+
+ public static class ShareCompat.IntentReader {
+ ctor public ShareCompat.IntentReader(android.app.Activity);
+ ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+ method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+ method public android.content.ComponentName? getCallingActivity();
+ method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+ method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+ method public CharSequence? getCallingApplicationLabel();
+ method public String? getCallingPackage();
+ method public String![]? getEmailBcc();
+ method public String![]? getEmailCc();
+ method public String![]? getEmailTo();
+ method public String? getHtmlText();
+ method public android.net.Uri? getStream();
+ method public android.net.Uri? getStream(int);
+ method public int getStreamCount();
+ method public String? getSubject();
+ method public CharSequence? getText();
+ method public String? getType();
+ method public boolean isMultipleShare();
+ method public boolean isShareIntent();
+ method public boolean isSingleShare();
+ }
+
+ public abstract class SharedElementCallback {
+ ctor public SharedElementCallback();
+ method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+ method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+ method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+ method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+ method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+ method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+ method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+ }
+
+ public static interface SharedElementCallback.OnSharedElementsReadyListener {
+ method public void onSharedElementsReady();
+ }
+
+ public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+ method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+ method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+ method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+ method public androidx.core.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+ method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+ method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+ method public android.content.Intent? editIntentAt(int);
+ method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+ method @Deprecated public android.content.Intent! getIntent(int);
+ method public int getIntentCount();
+ method public android.content.Intent![] getIntents();
+ method public android.app.PendingIntent? getPendingIntent(int, int);
+ method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+ method @Deprecated public java.util.Iterator<android.content.Intent!> iterator();
+ method public void startActivities();
+ method public void startActivities(android.os.Bundle?);
+ }
+
+ public static interface TaskStackBuilder.SupportParentable {
+ method public android.content.Intent? getSupportParentActivityIntent();
+ }
+
+}
+
+package androidx.core.content {
+
+ public final class ContentProviderCompat {
+ method public static android.content.Context requireContext(android.content.ContentProvider);
+ }
+
+ public final class ContentResolverCompat {
+ method public static android.database.Cursor? query(android.content.ContentResolver, android.net.Uri, String![]?, String?, String![]?, String?, androidx.core.os.CancellationSignal?);
+ }
+
+ public class ContextCompat {
+ ctor protected ContextCompat();
+ method public static int checkSelfPermission(android.content.Context, String);
+ method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+ method public static String? getAttributionTag(android.content.Context);
+ method public static java.io.File getCodeCacheDir(android.content.Context);
+ method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+ method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+ method public static android.content.Context getContextForLanguage(android.content.Context);
+ method public static java.io.File? getDataDir(android.content.Context);
+ method public static android.view.Display getDisplayOrDefault(@DisplayContext android.content.Context);
+ method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+ method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+ method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+ method public static java.util.concurrent.Executor getMainExecutor(android.content.Context);
+ method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+ method public static java.io.File![] getObbDirs(android.content.Context);
+ method public static String getString(android.content.Context, int);
+ method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+ method public static String? getSystemServiceName(android.content.Context, Class<?>);
+ method public static boolean isDeviceProtectedStorage(android.content.Context);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+ method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
+ method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+ method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+ method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+ method public static void startForegroundService(android.content.Context, android.content.Intent);
+ field public static final int RECEIVER_EXPORTED = 2; // 0x2
+ field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+ field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
+ }
+
+ public class FileProvider extends android.content.ContentProvider {
+ ctor public FileProvider();
+ ctor protected FileProvider(@XmlRes int);
+ method public int delete(android.net.Uri, String?, String![]?);
+ method public String? getType(android.net.Uri);
+ method public String? getTypeAnonymous(android.net.Uri);
+ method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+ method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+ method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues);
+ method public boolean onCreate();
+ method public android.database.Cursor query(android.net.Uri, String![]?, String?, String![]?, String?);
+ method public int update(android.net.Uri, android.content.ContentValues, String?, String![]?);
+ }
+
+ public final class IntentCompat {
+ method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+ method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+ method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+ method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
+ method public static android.content.Intent makeMainSelectorActivity(String, String);
+ field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+ field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+ field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+ field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+ field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+ }
+
+ public class IntentSanitizer {
+ method public android.content.Intent sanitize(android.content.Intent, androidx.core.util.Consumer<java.lang.String!>);
+ method public android.content.Intent sanitizeByFiltering(android.content.Intent);
+ method public android.content.Intent sanitizeByThrowing(android.content.Intent);
+ }
+
+ public static final class IntentSanitizer.Builder {
+ ctor public IntentSanitizer.Builder();
+ method public androidx.core.content.IntentSanitizer.Builder allowAction(androidx.core.util.Predicate<java.lang.String!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowAction(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowAnyComponent();
+ method public androidx.core.content.IntentSanitizer.Builder allowCategory(androidx.core.util.Predicate<java.lang.String!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowCategory(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowClipData(androidx.core.util.Predicate<android.content.ClipData!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowClipDataText();
+ method public androidx.core.content.IntentSanitizer.Builder allowClipDataUri(androidx.core.util.Predicate<android.net.Uri!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowClipDataUriWithAuthority(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowComponent(android.content.ComponentName);
+ method public androidx.core.content.IntentSanitizer.Builder allowComponent(androidx.core.util.Predicate<android.content.ComponentName!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowComponentWithPackage(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowData(androidx.core.util.Predicate<android.net.Uri!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowDataWithAuthority(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, androidx.core.util.Predicate<java.lang.Object!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<?>);
+ method public <T> androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<T!>, androidx.core.util.Predicate<T!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(androidx.core.util.Predicate<android.net.Uri!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtraStream(androidx.core.util.Predicate<android.net.Uri!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowExtraStreamUriWithAuthority(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowFlags(int);
+ method public androidx.core.content.IntentSanitizer.Builder allowHistoryStackFlags();
+ method public androidx.core.content.IntentSanitizer.Builder allowIdentifier();
+ method public androidx.core.content.IntentSanitizer.Builder allowPackage(androidx.core.util.Predicate<java.lang.String!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowPackage(String);
+ method public androidx.core.content.IntentSanitizer.Builder allowReceiverFlags();
+ method public androidx.core.content.IntentSanitizer.Builder allowSelector();
+ method public androidx.core.content.IntentSanitizer.Builder allowSourceBounds();
+ method public androidx.core.content.IntentSanitizer.Builder allowType(androidx.core.util.Predicate<java.lang.String!>);
+ method public androidx.core.content.IntentSanitizer.Builder allowType(String);
+ method public androidx.core.content.IntentSanitizer build();
+ }
+
+ public final class LocusIdCompat {
+ ctor public LocusIdCompat(String);
+ method public String getId();
+ method @RequiresApi(29) public android.content.LocusId toLocusId();
+ method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+ }
+
+ public final class MimeTypeFilter {
+ method public static boolean matches(String?, String);
+ method public static String? matches(String?, String![]);
+ method public static String? matches(String![]?, String);
+ method public static String![] matchesMany(String![]?, String);
+ }
+
+ public interface OnConfigurationChangedProvider {
+ method public void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+ method public void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+ }
+
+ public interface OnTrimMemoryProvider {
+ method public void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+ method public void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+ }
+
+ public final class PackageManagerCompat {
+ method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> getUnusedAppRestrictionsStatus(android.content.Context);
+ field public static final String ACTION_PERMISSION_REVOCATION_SETTINGS = "android.intent.action.AUTO_REVOKE_PERMISSIONS";
+ }
+
+ public final class PermissionChecker {
+ method public static int checkCallingOrSelfPermission(android.content.Context, String);
+ method public static int checkCallingPermission(android.content.Context, String, String?);
+ method public static int checkPermission(android.content.Context, String, int, int, String?);
+ method public static int checkSelfPermission(android.content.Context, String);
+ field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+ field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+ field public static final int PERMISSION_GRANTED = 0; // 0x0
+ }
+
+ @Deprecated public final class SharedPreferencesCompat {
+ }
+
+ @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+ method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+ method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+ }
+
+ public class UnusedAppRestrictionsBackportCallback {
+ method public void onResult(boolean, boolean) throws android.os.RemoteException;
+ }
+
+ public abstract class UnusedAppRestrictionsBackportService extends android.app.Service {
+ ctor public UnusedAppRestrictionsBackportService();
+ method protected abstract void isPermissionRevocationEnabled(androidx.core.content.UnusedAppRestrictionsBackportCallback);
+ method public android.os.IBinder? onBind(android.content.Intent?);
+ field public static final String ACTION_UNUSED_APP_RESTRICTIONS_BACKPORT_CONNECTION = "android.support.unusedapprestrictions.action.CustomUnusedAppRestrictionsBackportService";
+ }
+
+ public final class UnusedAppRestrictionsConstants {
+ field public static final int API_30 = 4; // 0x4
+ field public static final int API_30_BACKPORT = 3; // 0x3
+ field public static final int API_31 = 5; // 0x5
+ field public static final int DISABLED = 2; // 0x2
+ field public static final int ERROR = 0; // 0x0
+ field public static final int FEATURE_NOT_AVAILABLE = 1; // 0x1
+ }
+
+ public class UriMatcherCompat {
+ method public static androidx.core.util.Predicate<android.net.Uri!> asPredicate(android.content.UriMatcher);
+ }
+
+}
+
+package androidx.core.content.pm {
+
+ @Deprecated public final class ActivityInfoCompat {
+ field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+ }
+
+ public final class PackageInfoCompat {
+ method public static long getLongVersionCode(android.content.pm.PackageInfo);
+ method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ }
+
+ public final class PermissionInfoCompat {
+ method public static int getProtection(android.content.pm.PermissionInfo);
+ method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+ }
+
+ public class ShortcutInfoCompat {
+ method public android.content.ComponentName? getActivity();
+ method public java.util.Set<java.lang.String!>? getCategories();
+ method public CharSequence? getDisabledMessage();
+ method public int getDisabledReason();
+ method public int getExcludedFromSurfaces();
+ method public android.os.PersistableBundle? getExtras();
+ method public String getId();
+ method public android.content.Intent getIntent();
+ method public android.content.Intent![] getIntents();
+ method public long getLastChangedTimestamp();
+ method public androidx.core.content.LocusIdCompat? getLocusId();
+ method public CharSequence? getLongLabel();
+ method public String getPackage();
+ method public int getRank();
+ method public CharSequence getShortLabel();
+ method public android.os.UserHandle? getUserHandle();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isCached();
+ method public boolean isDeclaredInManifest();
+ method public boolean isDynamic();
+ method public boolean isEnabled();
+ method public boolean isExcludedFromSurfaces(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 {
+ ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String, String, java.util.List<java.lang.String!>);
+ method public androidx.core.content.pm.ShortcutInfoCompat build();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+ 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 setExcludedFromSurfaces(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 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();
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setSliceUri(android.net.Uri);
+ }
+
+ public class ShortcutManagerCompat {
+ method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+ method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+ method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+ method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+ method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+ method public static int getIconMaxHeight(android.content.Context);
+ method public static int getIconMaxWidth(android.content.Context);
+ method public static int getMaxShortcutCountPerActivity(android.content.Context);
+ method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, int);
+ method public static boolean isRateLimitingActive(android.content.Context);
+ method public static boolean isRequestPinShortcutSupported(android.content.Context);
+ method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+ method public static void removeAllDynamicShortcuts(android.content.Context);
+ method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+ method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+ method public static void reportShortcutUsed(android.content.Context, String);
+ method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+ method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+ method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+ field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+ field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+ field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+ field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+ field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+ }
+
+}
+
+package androidx.core.content.res {
+
+ public final class ConfigurationHelper {
+ method public static int getDensityDpi(android.content.res.Resources);
+ }
+
+ public final class ResourcesCompat {
+ method public static void clearCachesForTheme(android.content.res.Resources.Theme);
+ method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+ method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+ method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+ method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+ method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+ method public static float getFloat(android.content.res.Resources, @DimenRes int);
+ method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+ method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+ field @AnyRes public static final int ID_NULL = 0; // 0x0
+ }
+
+ public abstract static class ResourcesCompat.FontCallback {
+ ctor public ResourcesCompat.FontCallback();
+ method public abstract void onFontRetrievalFailed(int);
+ method public abstract void onFontRetrieved(android.graphics.Typeface);
+ }
+
+ public static final class ResourcesCompat.ThemeCompat {
+ method public static void rebase(android.content.res.Resources.Theme);
+ }
+
+}
+
+package androidx.core.database {
+
+ public final class CursorWindowCompat {
+ method public static android.database.CursorWindow create(String?, long);
+ }
+
+ @Deprecated public final class DatabaseUtilsCompat {
+ method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+ method @Deprecated public static String! concatenateWhere(String!, String!);
+ }
+
+}
+
+package androidx.core.database.sqlite {
+
+ public final class SQLiteCursorCompat {
+ method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+ }
+
+}
+
+package androidx.core.graphics {
+
+ public final class BitmapCompat {
+ method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, android.graphics.Rect?, boolean);
+ method public static int getAllocationByteCount(android.graphics.Bitmap);
+ method public static boolean hasMipMap(android.graphics.Bitmap);
+ method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+ }
+
+ public class BlendModeColorFilterCompat {
+ method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+ }
+
+ public enum BlendModeCompat {
+ enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+ enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+ enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+ }
+
+ public final class ColorUtils {
+ method @ColorInt public static int HSLToColor(float[]);
+ method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+ method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+ method @ColorInt public static int M3HCTtoColor(float, float, float);
+ method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+ method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+ method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+ method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double);
+ method public static void XYZToLAB(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double, double[]);
+ method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+ method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+ method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+ method public static double calculateContrast(@ColorInt int, @ColorInt int);
+ method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+ method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+ method public static void colorToHSL(@ColorInt int, float[]);
+ method public static void colorToLAB(@ColorInt int, double[]);
+ method public static void colorToM3HCT(@ColorInt int, float[]);
+ method public static void colorToXYZ(@ColorInt int, double[]);
+ method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+ method public static int compositeColors(@ColorInt int, @ColorInt int);
+ method public static double distanceEuclidean(double[], double[]);
+ method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+ }
+
+ public final class Insets {
+ method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+ method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+ method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+ method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+ method public static androidx.core.graphics.Insets of(int, int, int, int);
+ method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+ method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+ method @RequiresApi(29) public android.graphics.Insets toPlatformInsets();
+ field public static final androidx.core.graphics.Insets NONE;
+ field public final int bottom;
+ field public final int left;
+ field public final int right;
+ field public final int top;
+ }
+
+ public final class PaintCompat {
+ method public static boolean hasGlyph(android.graphics.Paint, String);
+ method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+ }
+
+ public final class PathSegment {
+ ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+ method public android.graphics.PointF getEnd();
+ method public float getEndFraction();
+ method public android.graphics.PointF getStart();
+ method public float getStartFraction();
+ }
+
+ public final class PathUtils {
+ method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+ method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+ }
+
+ public class TypefaceCompat {
+ method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+ method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, @IntRange(from=1, to=1000) int, boolean);
+ }
+
+}
+
+package androidx.core.graphics.drawable {
+
+ public final class DrawableCompat {
+ method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+ method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+ method public static void clearColorFilter(android.graphics.drawable.Drawable);
+ method public static int getAlpha(android.graphics.drawable.Drawable);
+ method public static android.graphics.ColorFilter? getColorFilter(android.graphics.drawable.Drawable);
+ method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+ method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+ method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+ method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+ method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+ method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+ method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+ method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+ method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+ method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
+ method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+ method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+ }
+
+ public class IconCompat implements androidx.versionedparcelable.VersionedParcelable {
+ method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+ method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+ method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+ method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+ method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+ method public static androidx.core.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+ method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+ method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(String);
+ method public static androidx.core.graphics.drawable.IconCompat createWithData(byte[], int, int);
+ method public static androidx.core.graphics.drawable.IconCompat createWithResource(android.content.Context, @DrawableRes int);
+ method @DrawableRes public int getResId();
+ method public String getResPackage();
+ method public int getType();
+ method public android.net.Uri getUri();
+ method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+ method public void onPostParceling();
+ method public void onPreParceling(boolean);
+ method public androidx.core.graphics.drawable.IconCompat setTint(@ColorInt int);
+ method public androidx.core.graphics.drawable.IconCompat setTintList(android.content.res.ColorStateList?);
+ method public androidx.core.graphics.drawable.IconCompat setTintMode(android.graphics.PorterDuff.Mode?);
+ method public android.os.Bundle toBundle();
+ method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+ method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+ field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+ field public static final int TYPE_BITMAP = 1; // 0x1
+ field public static final int TYPE_DATA = 3; // 0x3
+ field public static final int TYPE_RESOURCE = 2; // 0x2
+ field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+ field public static final int TYPE_URI = 4; // 0x4
+ field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+ }
+
+ public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+ method public void draw(android.graphics.Canvas);
+ method public final android.graphics.Bitmap? getBitmap();
+ method public float getCornerRadius();
+ method public int getGravity();
+ method public int getOpacity();
+ method public final android.graphics.Paint getPaint();
+ method public boolean hasAntiAlias();
+ method public boolean hasMipMap();
+ method public boolean isCircular();
+ method public void setAlpha(int);
+ method public void setAntiAlias(boolean);
+ method public void setCircular(boolean);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setCornerRadius(float);
+ method public void setDither(boolean);
+ method public void setGravity(int);
+ method public void setMipMap(boolean);
+ method public void setTargetDensity(android.graphics.Canvas);
+ method public void setTargetDensity(android.util.DisplayMetrics);
+ method public void setTargetDensity(int);
+ }
+
+ public final class RoundedBitmapDrawableFactory {
+ method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+ method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+ method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+ }
+
+}
+
+package androidx.core.hardware.display {
+
+ public final class DisplayManagerCompat {
+ method public android.view.Display? getDisplay(int);
+ method public android.view.Display![] getDisplays();
+ method public android.view.Display![] getDisplays(String?);
+ method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+ field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+ }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManagerCompat {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+ method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+ }
+
+ @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+ ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+ method @Deprecated public void onAuthenticationError(int, CharSequence!);
+ method @Deprecated public void onAuthenticationFailed();
+ method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+ method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+ }
+
+ @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+ ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+ method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+ }
+
+ @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+ ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+ ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+ ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+ method @Deprecated public javax.crypto.Cipher? getCipher();
+ method @Deprecated public javax.crypto.Mac? getMac();
+ method @Deprecated public java.security.Signature? getSignature();
+ }
+
+}
+
+package androidx.core.location {
+
+ public abstract class GnssStatusCompat {
+ method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+ method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+ method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+ method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+ method public abstract int getConstellationType(@IntRange(from=0) int);
+ method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+ method @IntRange(from=0) public abstract int getSatelliteCount();
+ method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+ method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+ method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+ method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+ method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+ method public abstract boolean usedInFix(@IntRange(from=0) int);
+ method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+ method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+ field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+ field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+ field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+ field public static final int CONSTELLATION_GPS = 1; // 0x1
+ field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+ field public static final int CONSTELLATION_QZSS = 4; // 0x4
+ field public static final int CONSTELLATION_SBAS = 2; // 0x2
+ field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+ }
+
+ public abstract static class GnssStatusCompat.Callback {
+ ctor public GnssStatusCompat.Callback();
+ method public void onFirstFix(@IntRange(from=0) int);
+ method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+ method public void onStarted();
+ method public void onStopped();
+ }
+
+ public final class LocationCompat {
+ method public static float getBearingAccuracyDegrees(android.location.Location);
+ method public static long getElapsedRealtimeMillis(android.location.Location);
+ method public static long getElapsedRealtimeNanos(android.location.Location);
+ method @FloatRange(from=0.0) public static float getMslAltitudeAccuracyMeters(android.location.Location);
+ method public static double getMslAltitudeMeters(android.location.Location);
+ method public static float getSpeedAccuracyMetersPerSecond(android.location.Location);
+ method public static float getVerticalAccuracyMeters(android.location.Location);
+ method public static boolean hasBearingAccuracy(android.location.Location);
+ method public static boolean hasMslAltitude(android.location.Location);
+ method public static boolean hasMslAltitudeAccuracy(android.location.Location);
+ method public static boolean hasSpeedAccuracy(android.location.Location);
+ method public static boolean hasVerticalAccuracy(android.location.Location);
+ method public static boolean isMock(android.location.Location);
+ method public static void removeMslAltitude(android.location.Location);
+ method public static void removeMslAltitudeAccuracy(android.location.Location);
+ method public static void setBearingAccuracyDegrees(android.location.Location, float);
+ method public static void setMock(android.location.Location, boolean);
+ method public static void setMslAltitudeAccuracyMeters(android.location.Location, @FloatRange(from=0.0) float);
+ method public static void setMslAltitudeMeters(android.location.Location, double);
+ method public static void setSpeedAccuracyMetersPerSecond(android.location.Location, float);
+ method public static void setVerticalAccuracyMeters(android.location.Location, float);
+ field public static final String EXTRA_BEARING_ACCURACY = "bearingAccuracy";
+ field public static final String EXTRA_IS_MOCK = "mockLocation";
+ field public static final String EXTRA_MSL_ALTITUDE = "androidx.core.location.extra.MSL_ALTITUDE";
+ field public static final String EXTRA_MSL_ALTITUDE_ACCURACY = "androidx.core.location.extra.MSL_ALTITUDE_ACCURACY";
+ field public static final String EXTRA_SPEED_ACCURACY = "speedAccuracy";
+ field public static final String EXTRA_VERTICAL_ACCURACY = "verticalAccuracy";
+ }
+
+ public interface LocationListenerCompat extends android.location.LocationListener {
+ method public default void onStatusChanged(String, int, android.os.Bundle?);
+ }
+
+ public final class LocationManagerCompat {
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void getCurrentLocation(android.location.LocationManager, String, androidx.core.os.CancellationSignal?, java.util.concurrent.Executor, androidx.core.util.Consumer<android.location.Location!>);
+ method public static String? getGnssHardwareModelName(android.location.LocationManager);
+ method public static int getGnssYearOfHardware(android.location.LocationManager);
+ method public static boolean hasProvider(android.location.LocationManager, String);
+ method public static boolean isLocationEnabled(android.location.LocationManager);
+ method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
+ method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void removeUpdates(android.location.LocationManager, androidx.core.location.LocationListenerCompat);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, androidx.core.location.LocationListenerCompat, android.os.Looper);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, java.util.concurrent.Executor, androidx.core.location.LocationListenerCompat);
+ method @RequiresApi(android.os.Build.VERSION_CODES.N) public static void unregisterGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback);
+ method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+ }
+
+ public final class LocationRequestCompat {
+ method @IntRange(from=1) public long getDurationMillis();
+ method @IntRange(from=0) public long getIntervalMillis();
+ method @IntRange(from=0) public long getMaxUpdateDelayMillis();
+ method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
+ method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
+ method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+ method public int getQuality();
+ method @RequiresApi(31) public android.location.LocationRequest toLocationRequest();
+ method @RequiresApi(19) public android.location.LocationRequest? toLocationRequest(String);
+ field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+ field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+ field public static final int QUALITY_LOW_POWER = 104; // 0x68
+ }
+
+ public static final class LocationRequestCompat.Builder {
+ ctor public LocationRequestCompat.Builder(androidx.core.location.LocationRequestCompat);
+ ctor public LocationRequestCompat.Builder(long);
+ method public androidx.core.location.LocationRequestCompat build();
+ method public androidx.core.location.LocationRequestCompat.Builder clearMinUpdateIntervalMillis();
+ method public androidx.core.location.LocationRequestCompat.Builder setDurationMillis(@IntRange(from=1) long);
+ method public androidx.core.location.LocationRequestCompat.Builder setIntervalMillis(@IntRange(from=0) long);
+ method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdateDelayMillis(@IntRange(from=0) long);
+ method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+ method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
+ method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+ method public androidx.core.location.LocationRequestCompat.Builder setQuality(int);
+ }
+
+}
+
+package androidx.core.math {
+
+ public class MathUtils {
+ method public static int addExact(int, int);
+ method public static long addExact(long, long);
+ method public static double clamp(double, double, double);
+ method public static float clamp(float, float, float);
+ method public static int clamp(int, int, int);
+ method public static long clamp(long, long, long);
+ method public static int decrementExact(int);
+ method public static long decrementExact(long);
+ method public static int incrementExact(int);
+ method public static long incrementExact(long);
+ method public static int multiplyExact(int, int);
+ method public static long multiplyExact(long, long);
+ method public static int negateExact(int);
+ method public static long negateExact(long);
+ method public static int subtractExact(int, int);
+ method public static long subtractExact(long, long);
+ method public static int toIntExact(long);
+ }
+
+}
+
+package androidx.core.net {
+
+ public final class ConnectivityManagerCompat {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+ method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+ field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+ field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+ field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+ }
+
+ public final class MailTo {
+ method public String? getBcc();
+ method public String? getBody();
+ method public String? getCc();
+ method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+ method public String? getSubject();
+ method public String? getTo();
+ method public static boolean isMailTo(android.net.Uri?);
+ method public static boolean isMailTo(String?);
+ method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+ method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+ field public static final String MAILTO_SCHEME = "mailto:";
+ }
+
+ public class ParseException extends java.lang.RuntimeException {
+ field public final String response;
+ }
+
+ public final class TrafficStatsCompat {
+ method @Deprecated public static void clearThreadStatsTag();
+ method @Deprecated public static int getThreadStatsTag();
+ method @Deprecated public static void incrementOperationCount(int);
+ method @Deprecated public static void incrementOperationCount(int, int);
+ method @Deprecated public static void setThreadStatsTag(int);
+ method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+ method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+ method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+ method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+ }
+
+ public final class UriCompat {
+ method public static String toSafeString(android.net.Uri);
+ }
+
+}
+
+package androidx.core.os {
+
+ public class BuildCompat {
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+ method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+ method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
+ method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastSv2();
+ method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastT();
+ method @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastU();
+ method @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
+ field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
+ field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.R) public static final int R_EXTENSION_INT;
+ field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.S) public static final int S_EXTENSION_INT;
+ field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.TIRAMISU) public static final int T_EXTENSION_INT;
+ }
+
+ @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface BuildCompat.PrereleaseSdkCheck {
+ }
+
+ public final class BundleCompat {
+ method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+ method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+ method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+ method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+ method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+ method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+ }
+
+ public final class CancellationSignal {
+ ctor public CancellationSignal();
+ method public void cancel();
+ method public Object? getCancellationSignalObject();
+ method public boolean isCanceled();
+ method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+ method public void throwIfCanceled();
+ }
+
+ public static interface CancellationSignal.OnCancelListener {
+ method public void onCancel();
+ }
+
+ public final class ConfigurationCompat {
+ method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+ method public static void setLocales(android.content.res.Configuration, androidx.core.os.LocaleListCompat);
+ }
+
+ public final class EnvironmentCompat {
+ method public static String getStorageState(java.io.File);
+ field public static final String MEDIA_UNKNOWN = "unknown";
+ }
+
+ public final class ExecutorCompat {
+ method public static java.util.concurrent.Executor create(android.os.Handler);
+ }
+
+ public final class HandlerCompat {
+ method public static android.os.Handler createAsync(android.os.Looper);
+ method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+ method @RequiresApi(16) public static boolean hasCallbacks(android.os.Handler, Runnable);
+ method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+ }
+
+ public final class LocaleListCompat {
+ method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+ method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+ method public java.util.Locale? get(int);
+ method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+ method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+ method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+ method public java.util.Locale? getFirstMatch(String![]);
+ method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
+ method public boolean isEmpty();
+ method @RequiresApi(21) public static boolean matchesLanguageAndScript(java.util.Locale, java.util.Locale);
+ method @IntRange(from=0) public int size();
+ method public String toLanguageTags();
+ method public Object? unwrap();
+ method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+ method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+ }
+
+ public final class MessageCompat {
+ method public static boolean isAsynchronous(android.os.Message);
+ method public static void setAsynchronous(android.os.Message, boolean);
+ }
+
+ public class OperationCanceledException extends java.lang.RuntimeException {
+ ctor public OperationCanceledException();
+ ctor public OperationCanceledException(String?);
+ }
+
+ public final class ParcelCompat {
+ method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+ method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
+ method public static boolean readBoolean(android.os.Parcel);
+ method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
+ method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
+ method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
+ method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
+ method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+ method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
+ method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
+ method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
+ method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
+ method public static <T> android.util.SparseArray<T!>? readSparseArray(android.os.Parcel, ClassLoader?, Class<? extends T>);
+ method public static void writeBoolean(android.os.Parcel, boolean);
+ }
+
+ @Deprecated public final class ParcelableCompat {
+ method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+ }
+
+ @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+ method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+ method @Deprecated public T![]! newArray(int);
+ }
+
+ public final class ProcessCompat {
+ method public static boolean isApplicationUid(int);
+ }
+
+ @Deprecated public final class TraceCompat {
+ method @Deprecated public static void beginAsyncSection(String, int);
+ method @Deprecated public static void beginSection(String);
+ method @Deprecated public static void endAsyncSection(String, int);
+ method @Deprecated public static void endSection();
+ method @Deprecated public static boolean isEnabled();
+ method @Deprecated public static void setCounter(String, int);
+ }
+
+ @RequiresApi(17) public class UserHandleCompat {
+ method public static android.os.UserHandle getUserHandleForUid(int);
+ }
+
+ public class UserManagerCompat {
+ method public static boolean isUserUnlocked(android.content.Context);
+ }
+
+}
+
+package androidx.core.provider {
+
+ public final class DocumentsContractCompat {
+ method public static android.net.Uri? buildChildDocumentsUri(String, String?);
+ method public static android.net.Uri? buildChildDocumentsUriUsingTree(android.net.Uri, String);
+ method public static android.net.Uri? buildDocumentUri(String, String);
+ method public static android.net.Uri? buildDocumentUriUsingTree(android.net.Uri, String);
+ method public static android.net.Uri? buildTreeDocumentUri(String, String);
+ method public static android.net.Uri? createDocument(android.content.ContentResolver, android.net.Uri, String, String) throws java.io.FileNotFoundException;
+ method public static String? getDocumentId(android.net.Uri);
+ method public static String? getTreeDocumentId(android.net.Uri);
+ method public static boolean isDocumentUri(android.content.Context, android.net.Uri?);
+ method public static boolean isTreeUri(android.net.Uri);
+ method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static android.net.Uri? renameDocument(android.content.ContentResolver, android.net.Uri, String) throws java.io.FileNotFoundException;
+ }
+
+ public static final class DocumentsContractCompat.DocumentCompat {
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+ }
+
+ public final class FontRequest {
+ ctor public FontRequest(String, String, String, @ArrayRes int);
+ ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+ method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+ method @ArrayRes public int getCertificatesArrayResId();
+ method public String getProviderAuthority();
+ method public String getProviderPackage();
+ method public String getQuery();
+ }
+
+ public class FontsContractCompat {
+ method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+ method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+ }
+
+ public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+ ctor public FontsContractCompat.Columns();
+ field public static final String FILE_ID = "file_id";
+ field public static final String ITALIC = "font_italic";
+ field public static final String RESULT_CODE = "result_code";
+ field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+ field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+ field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+ field public static final int RESULT_CODE_OK = 0; // 0x0
+ field public static final String TTC_INDEX = "font_ttc_index";
+ field public static final String VARIATION_SETTINGS = "font_variation_settings";
+ field public static final String WEIGHT = "font_weight";
+ }
+
+ public static class FontsContractCompat.FontFamilyResult {
+ method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+ method public int getStatusCode();
+ field public static final int STATUS_OK = 0; // 0x0
+ field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+ field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+ }
+
+ public static class FontsContractCompat.FontInfo {
+ method public int getResultCode();
+ method @IntRange(from=0) public int getTtcIndex();
+ method public android.net.Uri getUri();
+ method @IntRange(from=1, to=1000) public int getWeight();
+ method public boolean isItalic();
+ }
+
+ public static class FontsContractCompat.FontRequestCallback {
+ ctor public FontsContractCompat.FontRequestCallback();
+ method public void onTypefaceRequestFailed(int);
+ method public void onTypefaceRetrieved(android.graphics.Typeface!);
+ field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+ field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+ field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+ field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+ field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+ field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+ field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+ }
+
+}
+
+package androidx.core.telephony {
+
+ @RequiresApi(22) public class SubscriptionManagerCompat {
+ method public static int getSlotIndex(int);
+ }
+
+ public class TelephonyManagerCompat {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static String? getImei(android.telephony.TelephonyManager);
+ method public static int getSubscriptionId(android.telephony.TelephonyManager);
+ }
+
+}
+
+package androidx.core.telephony.mbms {
+
+ public final class MbmsHelper {
+ method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+ }
+
+}
+
+package androidx.core.text {
+
+ public final class BidiFormatter {
+ method public static androidx.core.text.BidiFormatter! getInstance();
+ method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+ method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+ method public boolean getStereoReset();
+ method public boolean isRtl(CharSequence!);
+ method public boolean isRtl(String!);
+ method public boolean isRtlContext();
+ method public CharSequence! unicodeWrap(CharSequence!);
+ method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+ method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+ method public CharSequence! unicodeWrap(CharSequence!, boolean);
+ method public String! unicodeWrap(String!);
+ method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+ method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+ method public String! unicodeWrap(String!, boolean);
+ }
+
+ public static final class BidiFormatter.Builder {
+ ctor public BidiFormatter.Builder();
+ ctor public BidiFormatter.Builder(boolean);
+ ctor public BidiFormatter.Builder(java.util.Locale!);
+ method public androidx.core.text.BidiFormatter! build();
+ method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+ method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+ }
+
+ public final class HtmlCompat {
+ method public static android.text.Spanned fromHtml(String, int);
+ method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+ method public static String toHtml(android.text.Spanned, int);
+ field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+ field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+ field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+ field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+ field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+ field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+ }
+
+ public final class ICUCompat {
+ method public static String? maximizeAndGetScript(java.util.Locale);
+ }
+
+ public class PrecomputedTextCompat implements android.text.Spannable {
+ method public char charAt(int);
+ method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+ method @IntRange(from=0) public int getParagraphCount();
+ method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+ method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+ method public int getSpanEnd(Object!);
+ method public int getSpanFlags(Object!);
+ method public int getSpanStart(Object!);
+ method public <T> T![]! getSpans(int, int, Class<T!>!);
+ method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+ method public int length();
+ method public int nextSpanTransition(int, int, Class!);
+ method public void removeSpan(Object!);
+ method public void setSpan(Object!, int, int, int);
+ method public CharSequence! subSequence(int, int);
+ }
+
+ public static final class PrecomputedTextCompat.Params {
+ ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+ method @RequiresApi(23) public int getBreakStrategy();
+ method @RequiresApi(23) public int getHyphenationFrequency();
+ method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+ method public android.text.TextPaint getTextPaint();
+ }
+
+ public static class PrecomputedTextCompat.Params.Builder {
+ ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+ method public androidx.core.text.PrecomputedTextCompat.Params build();
+ method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+ method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+ method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+ }
+
+ public interface TextDirectionHeuristicCompat {
+ method public boolean isRtl(char[]!, int, int);
+ method public boolean isRtl(CharSequence!, int, int);
+ }
+
+ public final class TextDirectionHeuristicsCompat {
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+ field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+ }
+
+ public final class TextUtilsCompat {
+ method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+ method public static String htmlEncode(String);
+ }
+
+}
+
+package androidx.core.text.method {
+
+ public class LinkMovementMethodCompat extends android.text.method.LinkMovementMethod {
+ method public static androidx.core.text.method.LinkMovementMethodCompat getInstance();
+ }
+
+}
+
+package androidx.core.text.util {
+
+ public final class LinkifyCompat {
+ method public static boolean addLinks(android.text.Spannable, int);
+ method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+ method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+ method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+ method public static boolean addLinks(android.widget.TextView, int);
+ method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+ method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+ method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+ }
+
+}
+
+package androidx.core.util {
+
+ public class AtomicFile {
+ ctor public AtomicFile(java.io.File);
+ method public void delete();
+ method public void failWrite(java.io.FileOutputStream?);
+ method public void finishWrite(java.io.FileOutputStream?);
+ method public java.io.File getBaseFile();
+ method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+ method public byte[] readFully() throws java.io.IOException;
+ method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+ }
+
+ public interface Consumer<T> {
+ method public void accept(T!);
+ }
+
+ public class ObjectsCompat {
+ method public static boolean equals(Object?, Object?);
+ method public static int hash(java.lang.Object!...);
+ method public static int hashCode(Object?);
+ method public static <T> T requireNonNull(T?);
+ method public static <T> T requireNonNull(T?, String);
+ method public static String? toString(Object?, String?);
+ }
+
+ public class Pair<F, S> {
+ ctor public Pair(F!, S!);
+ method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+ field public final F! first;
+ field public final S! second;
+ }
+
+ public final class PatternsCompat {
+ field public static final java.util.regex.Pattern DOMAIN_NAME;
+ field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+ field public static final java.util.regex.Pattern IP_ADDRESS;
+ field public static final java.util.regex.Pattern WEB_URL;
+ }
+
+ public final class Pools {
+ }
+
+ public static interface Pools.Pool<T> {
+ method public T? acquire();
+ method public boolean release(T);
+ }
+
+ public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+ ctor public Pools.SimplePool(int);
+ method public T! acquire();
+ method public boolean release(T);
+ }
+
+ public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+ ctor public Pools.SynchronizedPool(int);
+ }
+
+ public interface Predicate<T> {
+ method public default androidx.core.util.Predicate<T!>! and(androidx.core.util.Predicate<? super T>!);
+ method public static <T> androidx.core.util.Predicate<T!>! isEqual(Object!);
+ method public default androidx.core.util.Predicate<T!>! negate();
+ method public static <T> androidx.core.util.Predicate<T!>! not(androidx.core.util.Predicate<? super T>!);
+ method public default androidx.core.util.Predicate<T!>! or(androidx.core.util.Predicate<? super T>!);
+ method public boolean test(T!);
+ }
+
+ public final class SizeFCompat {
+ ctor public SizeFCompat(float, float);
+ method public float getHeight();
+ method public float getWidth();
+ method @RequiresApi(21) public android.util.SizeF toSizeF();
+ method @RequiresApi(21) public static androidx.core.util.SizeFCompat toSizeFCompat(android.util.SizeF);
+ }
+
+ public interface Supplier<T> {
+ method public T! get();
+ }
+
+}
+
+package androidx.core.view {
+
+ 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);
+ }
+
+ public abstract class ActionProvider {
+ 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 boolean onPerformDefaultAction();
+ method public void onPrepareSubMenu(android.view.SubMenu);
+ method public boolean overridesItemVisibility();
+ method public void refreshVisibility();
+ method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
+ }
+
+ public static interface ActionProvider.VisibilityListener {
+ method public void onActionProviderVisibilityChanged(boolean);
+ }
+
+ public final class ContentInfoCompat {
+ method public android.content.ClipData getClip();
+ method public android.os.Bundle? getExtras();
+ method public int getFlags();
+ method public android.net.Uri? getLinkUri();
+ method public int getSource();
+ method @RequiresApi(31) public static android.util.Pair<android.view.ContentInfo!,android.view.ContentInfo!> partition(android.view.ContentInfo, java.util.function.Predicate<android.content.ClipData.Item!>);
+ method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+ method @RequiresApi(31) public android.view.ContentInfo toContentInfo();
+ method @RequiresApi(31) public static androidx.core.view.ContentInfoCompat toContentInfoCompat(android.view.ContentInfo);
+ field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+ field public static final int SOURCE_APP = 0; // 0x0
+ field public static final int SOURCE_AUTOFILL = 4; // 0x4
+ field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+ field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+ field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+ field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
+ }
+
+ public static final class ContentInfoCompat.Builder {
+ ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+ ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+ method public androidx.core.view.ContentInfoCompat build();
+ method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+ method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+ method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+ method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+ method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+ }
+
+ public final class DisplayCompat {
+ method public static androidx.core.view.DisplayCompat.ModeCompat getMode(android.content.Context, android.view.Display);
+ method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+ }
+
+ public static final class DisplayCompat.ModeCompat {
+ method public int getPhysicalHeight();
+ method public int getPhysicalWidth();
+ method @Deprecated public boolean isNative();
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+ }
+
+ public final class DisplayCutoutCompat {
+ 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();
+ method public int getSafeInsetLeft();
+ method public int getSafeInsetRight();
+ method public int getSafeInsetTop();
+ method public androidx.core.graphics.Insets getWaterfallInsets();
+ }
+
+ public final class DragAndDropPermissionsCompat {
+ method public void release();
+ }
+
+ public class DragStartHelper {
+ 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);
+ }
+
+ public static interface DragStartHelper.OnDragStartListener {
+ method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
+ }
+
+ public final class GestureDetectorCompat {
+ ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+ ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler?);
+ method public boolean isLongpressEnabled();
+ method public boolean onTouchEvent(android.view.MotionEvent);
+ method public void setIsLongpressEnabled(boolean);
+ method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener?);
+ }
+
+ public final class GravityCompat {
+ method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+ method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+ method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+ method public static int getAbsoluteGravity(int, int);
+ field public static final int END = 8388613; // 0x800005
+ field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+ field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+ field public static final int START = 8388611; // 0x800003
+ }
+
+ public final class HapticFeedbackConstantsCompat {
+ field public static final int CLOCK_TICK = 4; // 0x4
+ field public static final int CONFIRM = 16; // 0x10
+ field public static final int CONTEXT_CLICK = 6; // 0x6
+ field public static final int DRAG_START = 25; // 0x19
+ field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
+ field public static final int GESTURE_END = 13; // 0xd
+ field public static final int GESTURE_START = 12; // 0xc
+ field public static final int GESTURE_THRESHOLD_ACTIVATE = 23; // 0x17
+ field public static final int GESTURE_THRESHOLD_DEACTIVATE = 24; // 0x18
+ field public static final int KEYBOARD_PRESS = 3; // 0x3
+ field public static final int KEYBOARD_RELEASE = 7; // 0x7
+ field public static final int KEYBOARD_TAP = 3; // 0x3
+ field public static final int LONG_PRESS = 0; // 0x0
+ field public static final int NO_HAPTICS = -1; // 0xffffffff
+ field public static final int REJECT = 17; // 0x11
+ field public static final int SEGMENT_FREQUENT_TICK = 27; // 0x1b
+ field public static final int SEGMENT_TICK = 26; // 0x1a
+ field public static final int TEXT_HANDLE_MOVE = 9; // 0x9
+ field public static final int TOGGLE_OFF = 22; // 0x16
+ field public static final int TOGGLE_ON = 21; // 0x15
+ field public static final int VIRTUAL_KEY = 1; // 0x1
+ field public static final int VIRTUAL_KEY_RELEASE = 8; // 0x8
+ }
+
+ public final class InputDeviceCompat {
+ field public static final int SOURCE_ANY = -256; // 0xffffff00
+ field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+ field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+ field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+ field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+ field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+ field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+ field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+ field public static final int SOURCE_DPAD = 513; // 0x201
+ field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+ field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+ field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+ field public static final int SOURCE_KEYBOARD = 257; // 0x101
+ field public static final int SOURCE_MOUSE = 8194; // 0x2002
+ field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+ field public static final int SOURCE_STYLUS = 16386; // 0x4002
+ field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+ field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+ field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+ field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+ field public static final int SOURCE_UNKNOWN = 0; // 0x0
+ }
+
+ public final class LayoutInflaterCompat {
+ method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+ method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+ method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+ }
+
+ @Deprecated public interface LayoutInflaterFactory {
+ method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+ }
+
+ public final class MarginLayoutParamsCompat {
+ method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+ method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+ method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+ method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+ method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+ method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+ method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+ method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+ }
+
+ public final class MenuCompat {
+ method public static void setGroupDividerEnabled(android.view.Menu, boolean);
+ method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+ }
+
+ public interface MenuHost {
+ method public void addMenuProvider(androidx.core.view.MenuProvider);
+ method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+ method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+ method public void invalidateMenu();
+ method public void removeMenuProvider(androidx.core.view.MenuProvider);
+ }
+
+ public class MenuHostHelper {
+ ctor public MenuHostHelper(Runnable);
+ method public void addMenuProvider(androidx.core.view.MenuProvider);
+ method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+ method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+ method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+ method public void onMenuClosed(android.view.Menu);
+ method public boolean onMenuItemSelected(android.view.MenuItem);
+ method public void onPrepareMenu(android.view.Menu);
+ method public void removeMenuProvider(androidx.core.view.MenuProvider);
+ }
+
+ public final class MenuItemCompat {
+ method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+ method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+ method public static androidx.core.view.ActionProvider? getActionProvider(android.view.MenuItem);
+ method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+ method public static int getAlphabeticModifiers(android.view.MenuItem);
+ method public static CharSequence? getContentDescription(android.view.MenuItem);
+ method public static android.content.res.ColorStateList? getIconTintList(android.view.MenuItem);
+ method public static android.graphics.PorterDuff.Mode? getIconTintMode(android.view.MenuItem);
+ method public static int getNumericModifiers(android.view.MenuItem);
+ method public static CharSequence? getTooltipText(android.view.MenuItem);
+ method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+ method public static android.view.MenuItem? setActionProvider(android.view.MenuItem, androidx.core.view.ActionProvider?);
+ method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+ method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+ method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+ method public static void setContentDescription(android.view.MenuItem, CharSequence?);
+ method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList?);
+ method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode?);
+ method public static void setNumericShortcut(android.view.MenuItem, char, int);
+ method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+ method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+ method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+ method public static void setTooltipText(android.view.MenuItem, CharSequence?);
+ field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+ field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+ field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+ field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+ field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+ }
+
+ @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+ method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+ method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+ }
+
+ public interface MenuProvider {
+ method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+ method public default void onMenuClosed(android.view.Menu);
+ method public boolean onMenuItemSelected(android.view.MenuItem);
+ method public default void onPrepareMenu(android.view.Menu);
+ }
+
+ public final class MotionEventCompat {
+ method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+ method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+ method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+ method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+ method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+ method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+ method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+ method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+ method @Deprecated public static int getSource(android.view.MotionEvent!);
+ method @Deprecated public static float getX(android.view.MotionEvent!, int);
+ method @Deprecated public static float getY(android.view.MotionEvent!, int);
+ method public static boolean isFromSource(android.view.MotionEvent, int);
+ field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+ field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+ field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+ field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+ field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+ field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+ field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+ field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+ field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+ field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+ field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+ field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+ field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+ field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+ field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+ field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+ field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+ field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+ field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+ field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+ field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+ field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+ field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+ field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+ field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+ field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+ field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+ field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+ field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+ field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+ field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+ field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+ field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+ field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+ field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+ field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+ field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+ field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+ field @Deprecated public static final int AXIS_RX = 12; // 0xc
+ field @Deprecated public static final int AXIS_RY = 13; // 0xd
+ field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+ field public static final int AXIS_SCROLL = 26; // 0x1a
+ field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+ field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+ field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+ field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+ field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+ field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+ field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+ field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+ field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+ field @Deprecated public static final int AXIS_X = 0; // 0x0
+ field @Deprecated public static final int AXIS_Y = 1; // 0x1
+ field @Deprecated public static final int AXIS_Z = 11; // 0xb
+ field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+ }
+
+ public interface NestedScrollingChild {
+ method public boolean dispatchNestedFling(float, float, boolean);
+ method public boolean dispatchNestedPreFling(float, float);
+ method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+ method public boolean hasNestedScrollingParent();
+ method public boolean isNestedScrollingEnabled();
+ method public void setNestedScrollingEnabled(boolean);
+ method public boolean startNestedScroll(int);
+ method public void stopNestedScroll();
+ }
+
+ public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+ method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+ method public boolean hasNestedScrollingParent(int);
+ method public boolean startNestedScroll(int, int);
+ method public void stopNestedScroll(int);
+ }
+
+ public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+ method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+ }
+
+ public class NestedScrollingChildHelper {
+ ctor public NestedScrollingChildHelper(android.view.View);
+ method public boolean dispatchNestedFling(float, float, boolean);
+ method public boolean dispatchNestedPreFling(float, float);
+ method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+ method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+ method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]?);
+ method public boolean hasNestedScrollingParent();
+ method public boolean hasNestedScrollingParent(int);
+ method public boolean isNestedScrollingEnabled();
+ method public void onDetachedFromWindow();
+ method public void onStopNestedScroll(android.view.View);
+ method public void setNestedScrollingEnabled(boolean);
+ method public boolean startNestedScroll(int);
+ method public boolean startNestedScroll(int, int);
+ method public void stopNestedScroll();
+ method public void stopNestedScroll(int);
+ }
+
+ public interface NestedScrollingParent {
+ method public int getNestedScrollAxes();
+ method public boolean onNestedFling(android.view.View, float, float, boolean);
+ method public boolean onNestedPreFling(android.view.View, float, float);
+ method public void onNestedPreScroll(android.view.View, int, int, int[]);
+ method public void onNestedScroll(android.view.View, int, int, int, int);
+ method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+ method public boolean onStartNestedScroll(android.view.View, android.view.View, int);
+ method public void onStopNestedScroll(android.view.View);
+ }
+
+ public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+ method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+ method public void onNestedScroll(android.view.View, int, int, int, int, int);
+ method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+ method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+ method public void onStopNestedScroll(android.view.View, int);
+ }
+
+ public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+ method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+ }
+
+ public class NestedScrollingParentHelper {
+ ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+ method public int getNestedScrollAxes();
+ method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+ method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+ method public void onStopNestedScroll(android.view.View);
+ method public void onStopNestedScroll(android.view.View, int);
+ }
+
+ public interface OnApplyWindowInsetsListener {
+ method public androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+ }
+
+ public interface OnReceiveContentListener {
+ method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+ }
+
+ public interface OnReceiveContentViewBehavior {
+ method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+ }
+
+ public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+ method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+ method public boolean onPreDraw();
+ method public void onViewAttachedToWindow(android.view.View);
+ method public void onViewDetachedFromWindow(android.view.View);
+ method public void removeListener();
+ }
+
+ public final class PointerIconCompat {
+ method public static androidx.core.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+ method public static androidx.core.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+ method public static androidx.core.view.PointerIconCompat load(android.content.res.Resources, int);
+ field public static final int TYPE_ALIAS = 1010; // 0x3f2
+ field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int TYPE_ARROW = 1000; // 0x3e8
+ field public static final int TYPE_CELL = 1006; // 0x3ee
+ field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int TYPE_COPY = 1011; // 0x3f3
+ field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+ field public static final int TYPE_GRAB = 1020; // 0x3fc
+ field public static final int TYPE_GRABBING = 1021; // 0x3fd
+ field public static final int TYPE_HAND = 1002; // 0x3ea
+ field public static final int TYPE_HELP = 1003; // 0x3eb
+ field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+ field public static final int TYPE_NULL = 0; // 0x0
+ field public static final int TYPE_TEXT = 1008; // 0x3f0
+ field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int TYPE_WAIT = 1004; // 0x3ec
+ field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
+ public final class ScaleGestureDetectorCompat {
+ method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+ method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+ method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+ method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+ }
+
+ public interface ScrollingView {
+ method public int computeHorizontalScrollExtent();
+ method public int computeHorizontalScrollOffset();
+ method public int computeHorizontalScrollRange();
+ method public int computeVerticalScrollExtent();
+ method public int computeVerticalScrollOffset();
+ method public int computeVerticalScrollRange();
+ }
+
+ public final class SoftwareKeyboardControllerCompat {
+ ctor public SoftwareKeyboardControllerCompat(android.view.View);
+ method public void hide();
+ method public void show();
+ }
+
+ public interface TintableBackgroundView {
+ method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @Deprecated public final class VelocityTrackerCompat {
+ method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+ method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+ }
+
+ public class ViewCompat {
+ ctor @Deprecated protected ViewCompat();
+ method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+ method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+ method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+ method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+ method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+ method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+ method public static void cancelDragAndDrop(android.view.View);
+ method @Deprecated public static int combineMeasuredStates(int, int);
+ method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+ method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+ method public static void dispatchFinishTemporaryDetach(android.view.View);
+ method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+ method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+ method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+ method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, int);
+ method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+ method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int);
+ method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int, int[]);
+ method public static void dispatchStartTemporaryDetach(android.view.View);
+ method public static void enableAccessibleClickableSpanSupport(android.view.View);
+ method public static int generateViewId();
+ method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+ method public static int getAccessibilityLiveRegion(android.view.View);
+ method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+ method @UiThread public static CharSequence? getAccessibilityPaneTitle(android.view.View);
+ method @Deprecated public static float getAlpha(android.view.View!);
+ method public static androidx.core.view.autofill.AutofillIdCompat? getAutofillId(android.view.View);
+ method public static android.content.res.ColorStateList? getBackgroundTintList(android.view.View);
+ method public static android.graphics.PorterDuff.Mode? getBackgroundTintMode(android.view.View);
+ method public static android.graphics.Rect? getClipBounds(android.view.View);
+ method public static androidx.core.view.contentcapture.ContentCaptureSessionCompat? getContentCaptureSession(android.view.View);
+ method public static android.view.Display? getDisplay(android.view.View);
+ method public static float getElevation(android.view.View);
+ method public static boolean getFitsSystemWindows(android.view.View);
+ method public static int getImportantForAccessibility(android.view.View);
+ method public static int getImportantForAutofill(android.view.View);
+ method public static int getImportantForContentCapture(android.view.View);
+ method public static int getLabelFor(android.view.View);
+ method @Deprecated public static int getLayerType(android.view.View!);
+ method public static int getLayoutDirection(android.view.View);
+ method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+ method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+ method @Deprecated public static int getMeasuredState(android.view.View!);
+ method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+ method public static int getMinimumHeight(android.view.View);
+ method public static int getMinimumWidth(android.view.View);
+ method public static int getNextClusterForwardId(android.view.View);
+ method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+ method @Deprecated public static int getOverScrollMode(android.view.View!);
+ method @Px public static int getPaddingEnd(android.view.View);
+ method @Px public static int getPaddingStart(android.view.View);
+ method public static android.view.ViewParent? getParentForAccessibility(android.view.View);
+ method @Deprecated public static float getPivotX(android.view.View!);
+ method @Deprecated public static float getPivotY(android.view.View!);
+ method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+ method @Deprecated public static float getRotation(android.view.View!);
+ method @Deprecated public static float getRotationX(android.view.View!);
+ method @Deprecated public static float getRotationY(android.view.View!);
+ method @Deprecated public static float getScaleX(android.view.View!);
+ method @Deprecated public static float getScaleY(android.view.View!);
+ method public static int getScrollIndicators(android.view.View);
+ method @UiThread public static CharSequence? getStateDescription(android.view.View);
+ method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+ method public static String? getTransitionName(android.view.View);
+ method @Deprecated public static float getTranslationX(android.view.View!);
+ method @Deprecated public static float getTranslationY(android.view.View!);
+ method public static float getTranslationZ(android.view.View);
+ method @Deprecated public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+ method @Deprecated public static int getWindowSystemUiVisibility(android.view.View);
+ method @Deprecated public static float getX(android.view.View!);
+ method @Deprecated public static float getY(android.view.View!);
+ method public static float getZ(android.view.View);
+ method public static boolean hasAccessibilityDelegate(android.view.View);
+ method public static boolean hasExplicitFocusable(android.view.View);
+ method public static boolean hasNestedScrollingParent(android.view.View);
+ method public static boolean hasNestedScrollingParent(android.view.View, int);
+ method public static boolean hasOnClickListeners(android.view.View);
+ method public static boolean hasOverlappingRendering(android.view.View);
+ method public static boolean hasTransientState(android.view.View);
+ method @UiThread public static boolean isAccessibilityHeading(android.view.View);
+ method public static boolean isAttachedToWindow(android.view.View);
+ method public static boolean isFocusedByDefault(android.view.View);
+ method public static boolean isImportantForAccessibility(android.view.View);
+ method public static boolean isImportantForAutofill(android.view.View);
+ method public static boolean isImportantForContentCapture(android.view.View);
+ method public static boolean isInLayout(android.view.View);
+ method public static boolean isKeyboardNavigationCluster(android.view.View);
+ method public static boolean isLaidOut(android.view.View);
+ method public static boolean isLayoutDirectionResolved(android.view.View);
+ method public static boolean isNestedScrollingEnabled(android.view.View);
+ method @Deprecated public static boolean isOpaque(android.view.View!);
+ method public static boolean isPaddingRelative(android.view.View);
+ method @UiThread public static boolean isScreenReaderFocusable(android.view.View);
+ method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+ method public static android.view.View? keyboardNavigationClusterSearch(android.view.View, android.view.View?, int);
+ method public static void offsetLeftAndRight(android.view.View, int);
+ method public static void offsetTopAndBottom(android.view.View, int);
+ method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+ method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle?);
+ method public static boolean performHapticFeedback(android.view.View, int);
+ method public static boolean performHapticFeedback(android.view.View, int, int);
+ method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+ method public static void postInvalidateOnAnimation(android.view.View);
+ method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+ method public static void postOnAnimation(android.view.View, Runnable);
+ method public static void postOnAnimationDelayed(android.view.View, Runnable, long);
+ method public static void removeAccessibilityAction(android.view.View, int);
+ method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+ method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+ method public static void requestApplyInsets(android.view.View);
+ method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+ method @Deprecated public static int resolveSizeAndState(int, int, int);
+ method public static boolean restoreDefaultFocus(android.view.View);
+ method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+ method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat?);
+ method @UiThread public static void setAccessibilityHeading(android.view.View, boolean);
+ method public static void setAccessibilityLiveRegion(android.view.View, int);
+ method @UiThread public static void setAccessibilityPaneTitle(android.view.View, CharSequence?);
+ method @Deprecated public static void setActivated(android.view.View!, boolean);
+ method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+ method public static void setAutofillHints(android.view.View, java.lang.String!...);
+ method public static void setAutofillId(android.view.View, androidx.core.view.autofill.AutofillIdCompat?);
+ method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+ method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList?);
+ method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode?);
+ method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+ method public static void setClipBounds(android.view.View, android.graphics.Rect?);
+ method public static void setContentCaptureSession(android.view.View, androidx.core.view.contentcapture.ContentCaptureSessionCompat?);
+ method public static void setElevation(android.view.View, float);
+ method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+ method public static void setFocusedByDefault(android.view.View, boolean);
+ method public static void setHasTransientState(android.view.View, boolean);
+ method @UiThread public static void setImportantForAccessibility(android.view.View, int);
+ method public static void setImportantForAutofill(android.view.View, int);
+ method public static void setImportantForContentCapture(android.view.View, int);
+ method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+ method public static void setLabelFor(android.view.View, @IdRes int);
+ method public static void setLayerPaint(android.view.View, android.graphics.Paint?);
+ method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+ method public static void setLayoutDirection(android.view.View, int);
+ method public static void setNestedScrollingEnabled(android.view.View, boolean);
+ method public static void setNextClusterForwardId(android.view.View, int);
+ method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+ method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+ method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+ method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+ method @Deprecated public static void setPivotX(android.view.View!, float);
+ method @Deprecated public static void setPivotY(android.view.View!, float);
+ method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat?);
+ method @Deprecated public static void setRotation(android.view.View!, float);
+ method @Deprecated public static void setRotationX(android.view.View!, float);
+ method @Deprecated public static void setRotationY(android.view.View!, float);
+ method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+ method @Deprecated public static void setScaleX(android.view.View!, float);
+ method @Deprecated public static void setScaleY(android.view.View!, float);
+ method @UiThread public static void setScreenReaderFocusable(android.view.View, boolean);
+ method public static void setScrollIndicators(android.view.View, int);
+ method public static void setScrollIndicators(android.view.View, int, int);
+ method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+ method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+ method public static void setTooltipText(android.view.View, CharSequence?);
+ method public static void setTransitionName(android.view.View, String?);
+ method @Deprecated public static void setTranslationX(android.view.View!, float);
+ method @Deprecated public static void setTranslationY(android.view.View!, float);
+ method public static void setTranslationZ(android.view.View, float);
+ method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+ method @Deprecated public static void setX(android.view.View!, float);
+ method @Deprecated public static void setY(android.view.View!, float);
+ method public static void setZ(android.view.View, float);
+ method public static boolean startDragAndDrop(android.view.View, android.content.ClipData?, android.view.View.DragShadowBuilder, Object?, int);
+ method public static boolean startNestedScroll(android.view.View, int);
+ method public static boolean startNestedScroll(android.view.View, int, int);
+ method public static void stopNestedScroll(android.view.View);
+ method public static void stopNestedScroll(android.view.View, int);
+ method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+ field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+ field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+ field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+ field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
+ field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+ field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+ field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+ field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+ field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+ field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+ field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+ field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+ field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+ field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+ field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+ field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+ field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+ field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+ field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+ field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+ field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+ field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+ field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+ field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+ field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+ field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+ field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+ field public static final int TYPE_NON_TOUCH = 1; // 0x1
+ field public static final int TYPE_TOUCH = 0; // 0x0
+ }
+
+ public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+ method public boolean onUnhandledKeyEvent(android.view.View, android.view.KeyEvent);
+ }
+
+ public final class ViewConfigurationCompat {
+ method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+ method public static int getScaledHoverSlop(android.view.ViewConfiguration);
+ method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+ method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+ method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+ method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context);
+ }
+
+ public final class ViewGroupCompat {
+ method public static int getLayoutMode(android.view.ViewGroup);
+ method public static int getNestedScrollAxes(android.view.ViewGroup);
+ method public static boolean isTransitionGroup(android.view.ViewGroup);
+ method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ method public static void setLayoutMode(android.view.ViewGroup, int);
+ method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+ method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+ field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+ field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+ }
+
+ public final class ViewParentCompat {
+ method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+ method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+ method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+ method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+ method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+ method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+ method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+ method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int, int[]);
+ method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+ method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+ method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+ method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+ method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+ method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+ method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ }
+
+ public final class ViewPropertyAnimatorCompat {
+ method public androidx.core.view.ViewPropertyAnimatorCompat alpha(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat alphaBy(float);
+ method public void cancel();
+ method public long getDuration();
+ method public android.view.animation.Interpolator? getInterpolator();
+ method public long getStartDelay();
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotation(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotationBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotationX(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotationXBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotationY(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat rotationYBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat scaleX(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat scaleXBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat scaleY(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat scaleYBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat setDuration(long);
+ method public androidx.core.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator?);
+ method public androidx.core.view.ViewPropertyAnimatorCompat setListener(androidx.core.view.ViewPropertyAnimatorListener?);
+ method public androidx.core.view.ViewPropertyAnimatorCompat setStartDelay(long);
+ method public androidx.core.view.ViewPropertyAnimatorCompat setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener?);
+ method public void start();
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationX(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationXBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationY(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationYBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationZ(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat translationZBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat withEndAction(Runnable);
+ method public androidx.core.view.ViewPropertyAnimatorCompat withLayer();
+ method public androidx.core.view.ViewPropertyAnimatorCompat withStartAction(Runnable);
+ method public androidx.core.view.ViewPropertyAnimatorCompat x(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat xBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat y(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat yBy(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat z(float);
+ method public androidx.core.view.ViewPropertyAnimatorCompat zBy(float);
+ }
+
+ public interface ViewPropertyAnimatorListener {
+ method public void onAnimationCancel(android.view.View);
+ method public void onAnimationEnd(android.view.View);
+ method public void onAnimationStart(android.view.View);
+ }
+
+ public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+ ctor public ViewPropertyAnimatorListenerAdapter();
+ method public void onAnimationCancel(android.view.View);
+ method public void onAnimationEnd(android.view.View);
+ method public void onAnimationStart(android.view.View);
+ }
+
+ public interface ViewPropertyAnimatorUpdateListener {
+ method public void onAnimationUpdate(android.view.View);
+ }
+
+ public class ViewStructureCompat {
+ method public void setClassName(String);
+ method public void setContentDescription(CharSequence);
+ method public void setDimens(int, int, int, int, int, int);
+ method public void setText(CharSequence);
+ method @RequiresApi(23) public android.view.ViewStructure toViewStructure();
+ method @RequiresApi(23) public static androidx.core.view.ViewStructureCompat toViewStructureCompat(android.view.ViewStructure);
+ }
+
+ public final class WindowCompat {
+ method public static androidx.core.view.WindowInsetsControllerCompat getInsetsController(android.view.Window, android.view.View);
+ method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+ method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+ field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+ field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+ field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+ }
+
+ public final class WindowInsetsAnimationCompat {
+ ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+ method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+ method public long getDurationMillis();
+ method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+ method public float getInterpolatedFraction();
+ method public android.view.animation.Interpolator? getInterpolator();
+ method public int getTypeMask();
+ method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+ method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+ }
+
+ public static final class WindowInsetsAnimationCompat.BoundsCompat {
+ ctor public WindowInsetsAnimationCompat.BoundsCompat(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+ method public androidx.core.graphics.Insets getLowerBound();
+ method public androidx.core.graphics.Insets getUpperBound();
+ method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat inset(androidx.core.graphics.Insets);
+ method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toBounds();
+ method @RequiresApi(30) public static androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat toBoundsCompat(android.view.WindowInsetsAnimation.Bounds);
+ }
+
+ public abstract static class WindowInsetsAnimationCompat.Callback {
+ ctor public WindowInsetsAnimationCompat.Callback(int);
+ method public final int getDispatchMode();
+ method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+ method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+ method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+ method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat);
+ field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+ field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+ }
+
+ public interface WindowInsetsAnimationControlListenerCompat {
+ method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+ method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+ method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+ }
+
+ public final class WindowInsetsAnimationControllerCompat {
+ method public void finish(boolean);
+ method public float getCurrentAlpha();
+ method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+ method public androidx.core.graphics.Insets getCurrentInsets();
+ method public androidx.core.graphics.Insets getHiddenStateInsets();
+ method public androidx.core.graphics.Insets getShownStateInsets();
+ method public int getTypes();
+ method public boolean isCancelled();
+ method public boolean isFinished();
+ method public boolean isReady();
+ method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+ }
+
+ public class WindowInsetsCompat {
+ ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+ method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+ method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+ method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+ method public androidx.core.graphics.Insets getInsets(int);
+ method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(int);
+ method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+ method @Deprecated public int getStableInsetBottom();
+ method @Deprecated public int getStableInsetLeft();
+ method @Deprecated public int getStableInsetRight();
+ method @Deprecated public int getStableInsetTop();
+ method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+ method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+ method @Deprecated public int getSystemWindowInsetBottom();
+ method @Deprecated public int getSystemWindowInsetLeft();
+ method @Deprecated public int getSystemWindowInsetRight();
+ method @Deprecated public int getSystemWindowInsetTop();
+ method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+ method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+ method public boolean hasInsets();
+ method @Deprecated public boolean hasStableInsets();
+ method @Deprecated public boolean hasSystemWindowInsets();
+ method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+ method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean isConsumed();
+ method public boolean isRound();
+ method public boolean isVisible(int);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+ method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+ method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+ method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+ field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+ }
+
+ public static final class WindowInsetsCompat.Builder {
+ ctor public WindowInsetsCompat.Builder();
+ ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+ method public androidx.core.view.WindowInsetsCompat build();
+ method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+ method public androidx.core.view.WindowInsetsCompat.Builder setInsets(int, androidx.core.graphics.Insets);
+ method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(int, androidx.core.graphics.Insets);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+ method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+ method public androidx.core.view.WindowInsetsCompat.Builder setVisible(int, boolean);
+ }
+
+ public static final class WindowInsetsCompat.Type {
+ method public static int captionBar();
+ method public static int displayCutout();
+ method public static int ime();
+ method public static int mandatorySystemGestures();
+ method public static int navigationBars();
+ method public static int statusBars();
+ method public static int systemBars();
+ method public static int systemGestures();
+ method public static int tappableElement();
+ }
+
+ public final class WindowInsetsControllerCompat {
+ ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+ method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+ method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+ method public int getSystemBarsBehavior();
+ method public void hide(int);
+ method public boolean isAppearanceLightNavigationBars();
+ method public boolean isAppearanceLightStatusBars();
+ method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+ method public void setAppearanceLightNavigationBars(boolean);
+ method public void setAppearanceLightStatusBars(boolean);
+ method public void setSystemBarsBehavior(int);
+ method public void show(int);
+ method @Deprecated @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+ field public static final int BEHAVIOR_DEFAULT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+ field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+ }
+
+ public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+ method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+ }
+
+}
+
+package androidx.core.view.accessibility {
+
+ public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+ method public void onClick(android.view.View);
+ }
+
+ public final class AccessibilityEventCompat {
+ method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+ method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+ method public static int getAction(android.view.accessibility.AccessibilityEvent);
+ method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+ method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+ method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+ method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+ method public static void setAction(android.view.accessibility.AccessibilityEvent, int);
+ method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+ method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+ field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+ field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
+ field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
+ field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+ field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+ field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+ field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+ field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+ field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+ field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+ field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+ field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+ field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+ field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+ field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+ field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+ field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+ field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+ field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+ field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+ field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+ field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+ field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+ field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+ field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+ field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+ field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+ field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+ field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+ field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+ }
+
+ public final class AccessibilityManagerCompat {
+ method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+ method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+ method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+ method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+ method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+ method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+ method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+ }
+
+ @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+ method @Deprecated public void onAccessibilityStateChanged(boolean);
+ }
+
+ @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+ ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+ }
+
+ public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+ method public void onTouchExplorationStateChanged(boolean);
+ }
+
+ public class AccessibilityNodeInfoCompat {
+ ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+ method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+ method public void addAction(int);
+ method public void addChild(android.view.View!);
+ method public void addChild(android.view.View!, int);
+ method public boolean canOpenPopup();
+ method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+ method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+ method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+ method @Deprecated public int getActions();
+ method public java.util.List<java.lang.String!> getAvailableExtraData();
+ method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+ method public void getBoundsInScreen(android.graphics.Rect!);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+ method public int getChildCount();
+ method public CharSequence! getClassName();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+ method public CharSequence! getContentDescription();
+ method public int getDrawingOrder();
+ method public CharSequence! getError();
+ method public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo? getExtraRenderingInfo();
+ method public android.os.Bundle! getExtras();
+ method public CharSequence? getHintText();
+ method @Deprecated public Object! getInfo();
+ method public int getInputType();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+ method public int getLiveRegion();
+ method public int getMaxTextLength();
+ method public long getMinDurationBetweenContentChangesMillis();
+ method public int getMovementGranularities();
+ method public CharSequence! getPackageName();
+ method public CharSequence? getPaneTitle();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+ method public CharSequence? getRoleDescription();
+ method public CharSequence? getStateDescription();
+ method public CharSequence! getText();
+ method public int getTextSelectionEnd();
+ method public int getTextSelectionStart();
+ method public CharSequence? getTooltipText();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+ method public String? getUniqueId();
+ method public String! getViewIdResourceName();
+ method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+ method public int getWindowId();
+ method public boolean hasRequestInitialAccessibilityFocus();
+ method public boolean isAccessibilityFocused();
+ method public boolean isCheckable();
+ method public boolean isChecked();
+ method public boolean isClickable();
+ method public boolean isContentInvalid();
+ method public boolean isContextClickable();
+ method public boolean isDismissable();
+ method public boolean isEditable();
+ method public boolean isEnabled();
+ method public boolean isFocusable();
+ method public boolean isFocused();
+ method public boolean isHeading();
+ method public boolean isImportantForAccessibility();
+ method public boolean isLongClickable();
+ method public boolean isMultiLine();
+ method public boolean isPassword();
+ method public boolean isScreenReaderFocusable();
+ method public boolean isScrollable();
+ method public boolean isSelected();
+ method public boolean isShowingHintText();
+ method public boolean isTextEntryKey();
+ method public boolean isTextSelectable();
+ method public boolean isVisibleToUser();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+ method public boolean performAction(int);
+ method public boolean performAction(int, android.os.Bundle!);
+ method @Deprecated public void recycle();
+ method public boolean refresh();
+ method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+ method public boolean removeChild(android.view.View!);
+ method public boolean removeChild(android.view.View!, int);
+ method public void setAccessibilityFocused(boolean);
+ method public void setAvailableExtraData(java.util.List<java.lang.String!>);
+ method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+ method public void setBoundsInScreen(android.graphics.Rect!);
+ method public void setCanOpenPopup(boolean);
+ method public void setCheckable(boolean);
+ method public void setChecked(boolean);
+ method public void setClassName(CharSequence!);
+ method public void setClickable(boolean);
+ method public void setCollectionInfo(Object!);
+ method public void setCollectionItemInfo(Object!);
+ method public void setContentDescription(CharSequence!);
+ method public void setContentInvalid(boolean);
+ method public void setContextClickable(boolean);
+ method public void setDismissable(boolean);
+ method public void setDrawingOrder(int);
+ method public void setEditable(boolean);
+ method public void setEnabled(boolean);
+ method public void setError(CharSequence!);
+ method public void setFocusable(boolean);
+ method public void setFocused(boolean);
+ method public void setHeading(boolean);
+ method public void setHintText(CharSequence?);
+ method public void setImportantForAccessibility(boolean);
+ method public void setInputType(int);
+ method public void setLabelFor(android.view.View!);
+ method public void setLabelFor(android.view.View!, int);
+ method public void setLabeledBy(android.view.View!);
+ method public void setLabeledBy(android.view.View!, int);
+ method public void setLiveRegion(int);
+ method public void setLongClickable(boolean);
+ method public void setMaxTextLength(int);
+ method public void setMinDurationBetweenContentChangesMillis(long);
+ method public void setMovementGranularities(int);
+ method public void setMultiLine(boolean);
+ method public void setPackageName(CharSequence!);
+ method public void setPaneTitle(CharSequence?);
+ method public void setParent(android.view.View!);
+ method public void setParent(android.view.View!, int);
+ method public void setPassword(boolean);
+ method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+ method public void setRequestInitialAccessibilityFocus(boolean);
+ method public void setRoleDescription(CharSequence?);
+ method public void setScreenReaderFocusable(boolean);
+ method public void setScrollable(boolean);
+ method public void setSelected(boolean);
+ method public void setShowingHintText(boolean);
+ method public void setSource(android.view.View!);
+ method public void setSource(android.view.View!, int);
+ method public void setStateDescription(CharSequence?);
+ method public void setText(CharSequence!);
+ method public void setTextEntryKey(boolean);
+ method public void setTextSelectable(boolean);
+ method public void setTextSelection(int, int);
+ method public void setTooltipText(CharSequence?);
+ method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+ method public void setTraversalAfter(android.view.View!);
+ method public void setTraversalAfter(android.view.View!, int);
+ method public void setTraversalBefore(android.view.View!);
+ method public void setTraversalBefore(android.view.View!, int);
+ method public void setUniqueId(String?);
+ method public void setViewIdResourceName(String!);
+ method public void setVisibleToUser(boolean);
+ method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+ field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+ field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+ field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+ field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+ field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+ field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+ field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+ field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+ field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+ field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+ field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+ field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+ field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+ field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+ field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+ field public static final int ACTION_CLICK = 16; // 0x10
+ field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+ field public static final int ACTION_COPY = 16384; // 0x4000
+ field public static final int ACTION_CUT = 65536; // 0x10000
+ field public static final int ACTION_DISMISS = 1048576; // 0x100000
+ field public static final int ACTION_EXPAND = 262144; // 0x40000
+ field public static final int ACTION_FOCUS = 1; // 0x1
+ field public static final int ACTION_LONG_CLICK = 32; // 0x20
+ field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+ field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+ field public static final int ACTION_PASTE = 32768; // 0x8000
+ field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+ field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+ field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+ field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+ field public static final int ACTION_SELECT = 4; // 0x4
+ field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+ field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+ field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+ field public static final int FOCUS_INPUT = 1; // 0x1
+ field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+ field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+ field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+ field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+ field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+ }
+
+ public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+ ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+ method public int getId();
+ method public CharSequence! getLabel();
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_CANCEL;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_DROP;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_START;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_TEXT_SUGGESTIONS;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+ }
+
+ public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+ method public int getColumnCount();
+ method public int getRowCount();
+ method public int getSelectionMode();
+ method public boolean isHierarchical();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+ field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+ field public static final int SELECTION_MODE_NONE = 0; // 0x0
+ field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+ }
+
+ public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+ method public int getColumnIndex();
+ method public int getColumnSpan();
+ method public int getRowIndex();
+ method public int getRowSpan();
+ method @Deprecated public boolean isHeading();
+ method public boolean isSelected();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+ }
+
+ public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+ method public float getCurrent();
+ method public float getMax();
+ method public float getMin();
+ method public int getType();
+ method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+ field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+ field public static final int RANGE_TYPE_INT = 0; // 0x0
+ field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+ }
+
+ public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+ ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+ method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getRegionCount();
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+ }
+
+ public class AccessibilityNodeProviderCompat {
+ ctor public AccessibilityNodeProviderCompat();
+ ctor public AccessibilityNodeProviderCompat(Object?);
+ method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+ method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String, int);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+ method public Object? getProvider();
+ method public boolean performAction(int, int, android.os.Bundle?);
+ field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+ }
+
+ public class AccessibilityRecordCompat {
+ ctor @Deprecated public AccessibilityRecordCompat(Object!);
+ method @Deprecated public boolean equals(Object?);
+ method @Deprecated public int getAddedCount();
+ method @Deprecated public CharSequence! getBeforeText();
+ method @Deprecated public CharSequence! getClassName();
+ method @Deprecated public CharSequence! getContentDescription();
+ method @Deprecated public int getCurrentItemIndex();
+ method @Deprecated public int getFromIndex();
+ method @Deprecated public Object! getImpl();
+ method @Deprecated public int getItemCount();
+ method @Deprecated public int getMaxScrollX();
+ method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+ method @Deprecated public int getMaxScrollY();
+ method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+ method @Deprecated public android.os.Parcelable! getParcelableData();
+ method @Deprecated public int getRemovedCount();
+ method @Deprecated public int getScrollX();
+ method @Deprecated public int getScrollY();
+ method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+ method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+ method @Deprecated public int getToIndex();
+ method @Deprecated public int getWindowId();
+ method @Deprecated public int hashCode();
+ method @Deprecated public boolean isChecked();
+ method @Deprecated public boolean isEnabled();
+ method @Deprecated public boolean isFullScreen();
+ method @Deprecated public boolean isPassword();
+ method @Deprecated public boolean isScrollable();
+ method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+ method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+ method @Deprecated public void recycle();
+ method @Deprecated public void setAddedCount(int);
+ method @Deprecated public void setBeforeText(CharSequence!);
+ method @Deprecated public void setChecked(boolean);
+ method @Deprecated public void setClassName(CharSequence!);
+ method @Deprecated public void setContentDescription(CharSequence!);
+ method @Deprecated public void setCurrentItemIndex(int);
+ method @Deprecated public void setEnabled(boolean);
+ method @Deprecated public void setFromIndex(int);
+ method @Deprecated public void setFullScreen(boolean);
+ method @Deprecated public void setItemCount(int);
+ method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+ method @Deprecated public void setMaxScrollX(int);
+ method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+ method @Deprecated public void setMaxScrollY(int);
+ method @Deprecated public void setParcelableData(android.os.Parcelable!);
+ method @Deprecated public void setPassword(boolean);
+ method @Deprecated public void setRemovedCount(int);
+ method @Deprecated public void setScrollX(int);
+ method @Deprecated public void setScrollY(int);
+ method @Deprecated public void setScrollable(boolean);
+ method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View?, int);
+ method @Deprecated public void setSource(android.view.View!);
+ method @Deprecated public void setSource(android.view.View!, int);
+ method @Deprecated public void setToIndex(int);
+ }
+
+ public interface AccessibilityViewCommand {
+ method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+ }
+
+ public abstract static class AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.CommandArguments();
+ }
+
+ public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+ method public boolean getExtendSelection();
+ method public int getGranularity();
+ }
+
+ public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.MoveHtmlArguments();
+ method public String? getHTMLElement();
+ }
+
+ public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.MoveWindowArguments();
+ method public int getX();
+ method public int getY();
+ }
+
+ public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+ method public int getColumn();
+ method public int getRow();
+ }
+
+ public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.SetProgressArguments();
+ method public float getProgress();
+ }
+
+ public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.SetSelectionArguments();
+ method public int getEnd();
+ method public int getStart();
+ }
+
+ public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+ ctor public AccessibilityViewCommand.SetTextArguments();
+ method public CharSequence? getText();
+ }
+
+ public class AccessibilityWindowInfoCompat {
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getAnchor();
+ method public void getBoundsInScreen(android.graphics.Rect);
+ method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getChild(int);
+ method public int getChildCount();
+ method public int getDisplayId();
+ method public int getId();
+ method public int getLayer();
+ method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getParent();
+ method public void getRegionInScreen(android.graphics.Region);
+ method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot();
+ method public CharSequence? getTitle();
+ method public int getType();
+ method public boolean isAccessibilityFocused();
+ method public boolean isActive();
+ method public boolean isFocused();
+ method public boolean isInPictureInPictureMode();
+ method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain();
+ method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat?);
+ method @Deprecated public void recycle();
+ method public android.view.accessibility.AccessibilityWindowInfo? unwrap();
+ field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+ field public static final int TYPE_APPLICATION = 1; // 0x1
+ field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+ field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+ field public static final int TYPE_SYSTEM = 3; // 0x3
+ }
+
+}
+
+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);
+ }
+
+}
+
+package androidx.core.view.autofill {
+
+ public class AutofillIdCompat {
+ method @RequiresApi(26) public android.view.autofill.AutofillId toAutofillId();
+ method @RequiresApi(26) public static androidx.core.view.autofill.AutofillIdCompat toAutofillIdCompat(android.view.autofill.AutofillId);
+ }
+
+}
+
+package androidx.core.view.contentcapture {
+
+ public class ContentCaptureSessionCompat {
+ method public android.view.autofill.AutofillId? newAutofillId(long);
+ method public androidx.core.view.ViewStructureCompat? newVirtualViewStructure(android.view.autofill.AutofillId, long);
+ method public void notifyViewTextChanged(android.view.autofill.AutofillId, CharSequence?);
+ method public void notifyViewsAppeared(java.util.List<android.view.ViewStructure!>);
+ method public void notifyViewsDisappeared(long[]);
+ method @RequiresApi(29) public android.view.contentcapture.ContentCaptureSession toContentCaptureSession();
+ method @RequiresApi(29) public static androidx.core.view.contentcapture.ContentCaptureSessionCompat toContentCaptureSessionCompat(android.view.contentcapture.ContentCaptureSession, android.view.View);
+ }
+
+}
+
+package androidx.core.view.inputmethod {
+
+ public final class EditorInfoCompat {
+ ctor @Deprecated public EditorInfoCompat();
+ method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+ method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+ method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+ method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+ method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+ method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+ method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+ field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+ field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+ }
+
+ public final class InputConnectionCompat {
+ ctor @Deprecated public InputConnectionCompat();
+ method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+ method @Deprecated public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+ method public static android.view.inputmethod.InputConnection createWrapper(android.view.View, android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+ field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+ }
+
+ public static interface InputConnectionCompat.OnCommitContentListener {
+ method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+ }
+
+ public final class InputContentInfoCompat {
+ ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+ method public android.net.Uri getContentUri();
+ method public android.content.ClipDescription getDescription();
+ method public android.net.Uri? getLinkUri();
+ method public void releasePermission();
+ method public void requestPermission();
+ method public Object? unwrap();
+ method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+ }
+
+}
+
+package androidx.core.widget {
+
+ public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+ ctor public AutoScrollHelper(android.view.View);
+ method public abstract boolean canTargetScrollHorizontally(int);
+ method public abstract boolean canTargetScrollVertically(int);
+ method public boolean isEnabled();
+ method public boolean isExclusive();
+ method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+ method public abstract void scrollTargetBy(int, int);
+ method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+ method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+ method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+ method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+ method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+ method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+ method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+ method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+ method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+ method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+ method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+ field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+ field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+ field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+ field public static final float NO_MAX = 3.4028235E38f;
+ field public static final float NO_MIN = 0.0f;
+ field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+ }
+
+ public final class CheckedTextViewCompat {
+ method public static android.graphics.drawable.Drawable? getCheckMarkDrawable(android.widget.CheckedTextView);
+ method public static android.content.res.ColorStateList? getCheckMarkTintList(android.widget.CheckedTextView);
+ method public static android.graphics.PorterDuff.Mode? getCheckMarkTintMode(android.widget.CheckedTextView);
+ method public static void setCheckMarkTintList(android.widget.CheckedTextView, android.content.res.ColorStateList?);
+ method public static void setCheckMarkTintMode(android.widget.CheckedTextView, android.graphics.PorterDuff.Mode?);
+ }
+
+ public final class CompoundButtonCompat {
+ method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+ method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+ method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+ method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+ method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+ }
+
+ public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+ ctor public ContentLoadingProgressBar(android.content.Context);
+ ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+ method public void hide();
+ method public void onAttachedToWindow();
+ method public void onDetachedFromWindow();
+ method public void show();
+ }
+
+ public final class EdgeEffectCompat {
+ ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+ method public static android.widget.EdgeEffect create(android.content.Context, android.util.AttributeSet?);
+ method @Deprecated public boolean draw(android.graphics.Canvas!);
+ method @Deprecated public void finish();
+ method public static float getDistance(android.widget.EdgeEffect);
+ method @Deprecated public boolean isFinished();
+ method @Deprecated public boolean onAbsorb(int);
+ method public static void onPull(android.widget.EdgeEffect, float, float);
+ method @Deprecated public boolean onPull(float);
+ method @Deprecated public boolean onPull(float, float);
+ method public static float onPullDistance(android.widget.EdgeEffect, float, float);
+ method @Deprecated public boolean onRelease();
+ method @Deprecated public void setSize(int, int);
+ }
+
+ public class ImageViewCompat {
+ method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+ method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+ method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+ method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+ }
+
+ public final class ListPopupWindowCompat {
+ method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+ method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+ }
+
+ public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+ ctor public ListViewAutoScrollHelper(android.widget.ListView);
+ method public boolean canTargetScrollHorizontally(int);
+ method public boolean canTargetScrollVertically(int);
+ method public void scrollTargetBy(int, int);
+ }
+
+ public final class ListViewCompat {
+ method public static boolean canScrollList(android.widget.ListView, int);
+ method public static void scrollListBy(android.widget.ListView, int);
+ }
+
+ public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+ ctor public NestedScrollView(android.content.Context);
+ ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+ ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+ method public boolean arrowScroll(int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+ method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+ method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+ method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+ method public boolean executeKeyEvent(android.view.KeyEvent);
+ method public void fling(int);
+ method public boolean fullScroll(int);
+ method public int getMaxScrollAmount();
+ method public boolean hasNestedScrollingParent(int);
+ method public boolean isFillViewport();
+ method public boolean isSmoothScrollingEnabled();
+ method public void onAttachedToWindow();
+ method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+ method public void onNestedScroll(android.view.View, int, int, int, int, int);
+ method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+ method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+ method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+ method public void onStopNestedScroll(android.view.View, int);
+ method public boolean pageScroll(int);
+ method public void setFillViewport(boolean);
+ method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+ method public void setSmoothScrollingEnabled(boolean);
+ method public final void smoothScrollBy(int, int);
+ method public final void smoothScrollBy(int, int, int);
+ method public final void smoothScrollTo(int, int);
+ method public final void smoothScrollTo(int, int, int);
+ method public boolean startNestedScroll(int, int);
+ method public void stopNestedScroll(int);
+ }
+
+ public static interface NestedScrollView.OnScrollChangeListener {
+ method public void onScrollChange(androidx.core.widget.NestedScrollView, int, int, int, int);
+ }
+
+ public final class PopupMenuCompat {
+ method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+ }
+
+ public final class PopupWindowCompat {
+ method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+ method public static int getWindowLayoutType(android.widget.PopupWindow);
+ method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+ method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+ method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+ }
+
+ @Deprecated public final class ScrollerCompat {
+ method @Deprecated public void abortAnimation();
+ method @Deprecated public boolean computeScrollOffset();
+ method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+ method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+ method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+ method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+ method @Deprecated public float getCurrVelocity();
+ method @Deprecated public int getCurrX();
+ method @Deprecated public int getCurrY();
+ method @Deprecated public int getFinalX();
+ method @Deprecated public int getFinalY();
+ method @Deprecated public boolean isFinished();
+ method @Deprecated public boolean isOverScrolled();
+ method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+ method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+ method @Deprecated public boolean springBack(int, int, int, int, int, int);
+ method @Deprecated public void startScroll(int, int, int, int);
+ method @Deprecated public void startScroll(int, int, int, int, int);
+ }
+
+ public final class TextViewCompat {
+ method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+ method public static int getAutoSizeMinTextSize(android.widget.TextView);
+ method public static int getAutoSizeStepGranularity(android.widget.TextView);
+ method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+ method public static int getAutoSizeTextType(android.widget.TextView);
+ method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+ method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+ method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+ method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+ method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+ method public static int getMaxLines(android.widget.TextView);
+ method public static int getMinLines(android.widget.TextView);
+ method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+ method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+ method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+ method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+ method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+ method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+ method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+ method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+ method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+ method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+ method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+ method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+ method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+ field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+ field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+ }
+
+ public interface TintableCompoundButton {
+ method public android.content.res.ColorStateList? getSupportButtonTintList();
+ method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+ method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+ method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public interface TintableCompoundDrawablesView {
+ method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+}
+
diff --git a/core/core/api/restricted_1.11.0-beta01.txt b/core/core/api/restricted_1.11.0-beta01.txt
index f02ce1a..cc104ba 100644
--- a/core/core/api/restricted_1.11.0-beta01.txt
+++ b/core/core/api/restricted_1.11.0-beta01.txt
@@ -2195,19 +2195,12 @@
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
- method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastSv2();
- method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastT();
- method @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastU();
- method @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.R) public static final int R_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.S) public static final int S_EXTENSION_INT;
field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.TIRAMISU) public static final int T_EXTENSION_INT;
}
- @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface BuildCompat.PrereleaseSdkCheck {
- }
-
public final class BundleCompat {
method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index f02ce1a..c02961d 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -203,6 +203,15 @@
@IntDef(flag=true, value={androidx.core.app.FrameMetricsAggregator.TOTAL_DURATION, androidx.core.app.FrameMetricsAggregator.INPUT_DURATION, androidx.core.app.FrameMetricsAggregator.LAYOUT_MEASURE_DURATION, androidx.core.app.FrameMetricsAggregator.DRAW_DURATION, androidx.core.app.FrameMetricsAggregator.SYNC_DURATION, androidx.core.app.FrameMetricsAggregator.COMMAND_DURATION, androidx.core.app.FrameMetricsAggregator.SWAP_DURATION, androidx.core.app.FrameMetricsAggregator.DELAY_DURATION, androidx.core.app.FrameMetricsAggregator.ANIMATION_DURATION, androidx.core.app.FrameMetricsAggregator.EVERY_DURATION}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FrameMetricsAggregator.MetricType {
}
+ public final class GrammaticalInflectionManagerCompat {
+ method @AnyThread public static int getApplicationGrammaticalGender(android.content.Context);
+ method @AnyThread public static void setRequestedApplicationGrammaticalGender(android.content.Context, int);
+ field public static final int GRAMMATICAL_GENDER_FEMININE = 2; // 0x2
+ field public static final int GRAMMATICAL_GENDER_MASCULINE = 3; // 0x3
+ field public static final int GRAMMATICAL_GENDER_NEUTRAL = 1; // 0x1
+ field public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0; // 0x0
+ }
+
@Deprecated public abstract class JobIntentService extends android.app.Service {
ctor @Deprecated public JobIntentService();
method @Deprecated public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
@@ -892,6 +901,7 @@
public final class NotificationManagerCompat {
method public boolean areNotificationsEnabled();
+ method public boolean canUseFullScreenIntent();
method public void cancel(int);
method public void cancel(String?, int);
method public void cancelAll();
@@ -1073,6 +1083,7 @@
}
public final class ServiceCompat {
+ method public static void startForeground(android.app.Service, int, android.app.Notification, int);
method public static void stopForeground(android.app.Service, @androidx.core.app.ServiceCompat.StopForegroundFlags int);
field public static final int START_STICKY = 1; // 0x1
field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
@@ -1229,7 +1240,6 @@
ctor protected FileProvider(@XmlRes int);
method public int delete(android.net.Uri, String?, String![]?);
method public String? getType(android.net.Uri);
- method public String? getTypeAnonymous(android.net.Uri);
method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues);
@@ -2434,6 +2444,26 @@
}
+package androidx.core.service.quicksettings {
+
+ public class PendingIntentActivityWrapper {
+ ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+ ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, boolean);
+ method public android.content.Context getContext();
+ method public int getFlags();
+ method public android.content.Intent getIntent();
+ method public android.os.Bundle getOptions();
+ method public android.app.PendingIntent? getPendingIntent();
+ method public int getRequestCode();
+ method public boolean isMutable();
+ }
+
+ public class TileServiceCompat {
+ method public static void startActivityAndCollapse(android.service.quicksettings.TileService, androidx.core.service.quicksettings.PendingIntentActivityWrapper);
+ }
+
+}
+
package androidx.core.telephony {
@RequiresApi(22) public class SubscriptionManagerCompat {
@@ -2587,6 +2617,66 @@
@IntDef(flag=true, value={android.text.util.Linkify.WEB_URLS, android.text.util.Linkify.EMAIL_ADDRESSES, android.text.util.Linkify.PHONE_NUMBERS, android.text.util.Linkify.MAP_ADDRESSES, android.text.util.Linkify.ALL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinkifyCompat.LinkifyMask {
}
+ @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public final class LocalePreferences {
+ method public static String getCalendarType();
+ method public static String getCalendarType(boolean);
+ method public static String getCalendarType(java.util.Locale);
+ method public static String getCalendarType(java.util.Locale, boolean);
+ method public static String getFirstDayOfWeek();
+ method public static String getFirstDayOfWeek(boolean);
+ method public static String getFirstDayOfWeek(java.util.Locale);
+ method public static String getFirstDayOfWeek(java.util.Locale, boolean);
+ method public static String getHourCycle();
+ method public static String getHourCycle(boolean);
+ method public static String getHourCycle(java.util.Locale);
+ method public static String getHourCycle(java.util.Locale, boolean);
+ method public static String getTemperatureUnit();
+ method public static String getTemperatureUnit(boolean);
+ method public static String getTemperatureUnit(java.util.Locale);
+ method public static String getTemperatureUnit(java.util.Locale, boolean);
+ }
+
+ public static class LocalePreferences.CalendarType {
+ field public static final String CHINESE = "chinese";
+ field public static final String DANGI = "dangi";
+ field public static final String DEFAULT = "";
+ field public static final String GREGORIAN = "gregorian";
+ field public static final String HEBREW = "hebrew";
+ field public static final String INDIAN = "indian";
+ field public static final String ISLAMIC = "islamic";
+ field public static final String ISLAMIC_CIVIL = "islamic-civil";
+ field public static final String ISLAMIC_RGSA = "islamic-rgsa";
+ field public static final String ISLAMIC_TBLA = "islamic-tbla";
+ field public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+ field public static final String PERSIAN = "persian";
+ }
+
+ public static class LocalePreferences.FirstDayOfWeek {
+ field public static final String DEFAULT = "";
+ field public static final String FRIDAY = "fri";
+ field public static final String MONDAY = "mon";
+ field public static final String SATURDAY = "sat";
+ field public static final String SUNDAY = "sun";
+ field public static final String THURSDAY = "thu";
+ field public static final String TUESDAY = "tue";
+ field public static final String WEDNESDAY = "wed";
+ }
+
+ public static class LocalePreferences.HourCycle {
+ field public static final String DEFAULT = "";
+ field public static final String H11 = "h11";
+ field public static final String H12 = "h12";
+ field public static final String H23 = "h23";
+ field public static final String H24 = "h24";
+ }
+
+ public static class LocalePreferences.TemperatureUnit {
+ field public static final String CELSIUS = "celsius";
+ field public static final String DEFAULT = "";
+ field public static final String FAHRENHEIT = "fahrenhe";
+ field public static final String KELVIN = "kelvin";
+ }
+
}
package androidx.core.util {
@@ -2610,6 +2700,10 @@
method public static void buildShortClassTag(Object!, StringBuilder!);
}
+ @java.lang.FunctionalInterface public interface Function<T, R> {
+ method public R! apply(T!);
+ }
+
@Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
ctor @Deprecated public LogWriter(String!);
method @Deprecated public void close();
@@ -2710,6 +2804,14 @@
field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int HUNDRED_DAY_FIELD_LEN = 19; // 0x13
}
+ public class TypedValueCompat {
+ method public static float deriveDimension(int, float, android.util.DisplayMetrics);
+ method public static float dpToPx(float, android.util.DisplayMetrics);
+ method public static float pxToDp(float, android.util.DisplayMetrics);
+ method public static float pxToSp(float, android.util.DisplayMetrics);
+ method public static float spToPx(float, android.util.DisplayMetrics);
+ }
+
}
package androidx.core.view {
@@ -3223,9 +3325,15 @@
method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
}
- @Deprecated public final class VelocityTrackerCompat {
+ public final class VelocityTrackerCompat {
+ method public static float getAxisVelocity(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int);
+ method public static float getAxisVelocity(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int, int);
method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+ method public static boolean isAxisSupported(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int);
+ }
+
+ @IntDef({android.view.MotionEvent.AXIS_X, android.view.MotionEvent.AXIS_Y, android.view.MotionEvent.AXIS_SCROLL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VelocityTrackerCompat.VelocityTrackableMotionEventAxis {
}
public class ViewCompat {
@@ -3472,6 +3580,8 @@
public final class ViewConfigurationCompat {
method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
method public static int getScaledHoverSlop(android.view.ViewConfiguration);
+ method public static int getScaledMaximumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+ method public static int getScaledMinimumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
@@ -3776,6 +3886,7 @@
field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+ field public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 67108864; // 0x4000000
field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
@@ -3873,6 +3984,7 @@
method public boolean isEnabled();
method public boolean isFocusable();
method public boolean isFocused();
+ method public boolean isGranularScrollingSupported();
method public boolean isHeading();
method public boolean isImportantForAccessibility();
method public boolean isLongClickable();
@@ -3917,6 +4029,7 @@
method public void setError(CharSequence!);
method public void setFocusable(boolean);
method public void setFocused(boolean);
+ method public void setGranularScrollingSupported(boolean);
method public void setHeading(boolean);
method public void setHintText(CharSequence?);
method public void setImportantForAccessibility(boolean);
@@ -3963,6 +4076,7 @@
method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+ field public static final String ACTION_ARGUMENT_DIRECTION_INT = "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -3971,6 +4085,7 @@
field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+ field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -4048,6 +4163,7 @@
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+ field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
@@ -4222,6 +4338,7 @@
}
public class AccessibilityWindowInfoCompat {
+ ctor public AccessibilityWindowInfoCompat();
method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getAnchor();
method public void getBoundsInScreen(android.graphics.Rect);
method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getChild(int);
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 832085f..149662a 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -22,6 +22,9 @@
implementation("androidx.concurrent:concurrent-futures:1.0.0")
implementation("androidx.interpolator:interpolator:1.0.0")
+ // Workaround for Kotlin dependency constraints
+ implementation(libs.kotlinStdlib)
+
// We don't ship this as a public artifact, so it must remain a project-type dependency.
annotationProcessor(projectOrArtifact(":versionedparcelable:versionedparcelable-compiler"))
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index 15bf316..44d14fe 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -722,6 +722,15 @@
</issue>
<issue
+ id="Range"
+ message="Value must be ≥ 1 and ≤ 200 but `getSvid` can be 206"
+ errorLine1=" return mWrapped.getSvid(satelliteIndex);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/location/GnssStatusWrapper.java"/>
+ </issue>
+
+ <issue
id="WrongConstant"
message="Must be one of: Callback.DISPATCH_MODE_STOP, Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE"
errorLine1=" super(compat.getDispatchMode());"
@@ -2046,6 +2055,15 @@
<issue
id="ClassVerificationFailure"
+ message="This call references a method added in API level 23; however, the containing class androidx.core.app.NotificationManagerCompat is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" mContext.checkSelfPermission(Manifest.permission.USE_FULL_SCREEN_INTENT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" mWrapped = new PrecomputedText.Params.Builder(paint)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/core/core/src/androidTest/AndroidManifest.xml b/core/core/src/androidTest/AndroidManifest.xml
index 2688f36..30756c9 100644
--- a/core/core/src/androidTest/AndroidManifest.xml
+++ b/core/core/src/androidTest/AndroidManifest.xml
@@ -144,6 +144,11 @@
android:name="androidx.core.view.inputmethod.ImeSecondarySplitTestActivity"
android:exported="true" />
+ <activity
+ android:name="androidx.core.app.GrammaticalInfectionActivity"
+ android:configChanges="grammaticalGender"
+ android:exported="true" />
+
<activity-alias
android:name="androidx.core.app.NavUtilsAliasActivity"
android:targetActivity="androidx.core.app.NavUtilsActivity">
diff --git a/core/core/src/androidTest/java/androidx/core/app/GrammaticalInfectionActivity.java b/core/core/src/androidTest/java/androidx/core/app/GrammaticalInfectionActivity.java
new file mode 100644
index 0000000..07e58d1
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/app/GrammaticalInfectionActivity.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.app;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class GrammaticalInfectionActivity extends Activity {
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ setContentView(androidx.core.test.R.layout.activity_compat_activity);
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration config) {
+ super.onConfigurationChanged(config);
+ mCountDownLatch.countDown();
+ }
+
+ public void await() throws InterruptedException {
+ mCountDownLatch.await(5, TimeUnit.SECONDS);
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/app/GrammaticalInflectionManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/GrammaticalInflectionManagerCompatTest.java
new file mode 100644
index 0000000..187ca12
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/app/GrammaticalInflectionManagerCompatTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.app;
+
+import static androidx.core.app.GrammaticalInflectionManagerCompat.GRAMMATICAL_GENDER_MASCULINE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.v4.BaseInstrumentationTestCase;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GrammaticalInflectionManagerCompatTest extends
+ BaseInstrumentationTestCase<GrammaticalInfectionActivity> {
+
+ public GrammaticalInflectionManagerCompatTest() {
+ super(GrammaticalInfectionActivity.class);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 34)
+ public void testSetGrammaticalGender() throws InterruptedException {
+ GrammaticalInflectionManagerCompat.setRequestedApplicationGrammaticalGender(
+ mActivityTestRule.getActivity(),
+ GRAMMATICAL_GENDER_MASCULINE
+ );
+
+ mActivityTestRule.getActivity().await();
+
+ assertEquals(GRAMMATICAL_GENDER_MASCULINE,
+ GrammaticalInflectionManagerCompat.getApplicationGrammaticalGender(
+ mActivityTestRule.getActivity()));
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
index 6531103..a264df7 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
@@ -26,7 +26,10 @@
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_MAX;
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_MIN;
+import static org.mockito.Mockito.spy;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -34,6 +37,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -1159,6 +1163,27 @@
}
@Test
+ public void testCanUseFullScreenIntent() {
+ NotificationManager fakeManager = mock(NotificationManager.class);
+
+ Context spyContext = spy(mContext);
+
+ NotificationManagerCompat notificationManagerCompat =
+ new NotificationManagerCompat(fakeManager, spyContext);
+
+ final boolean canUse = notificationManagerCompat.canUseFullScreenIntent();
+
+ if (Build.VERSION.SDK_INT < 29) {
+ assertTrue(canUse);
+
+ } else if (Build.VERSION.SDK_INT < 34) {
+ verify(spyContext, times(1))
+ .checkSelfPermission(Manifest.permission.USE_FULL_SCREEN_INTENT);
+ } else {
+ verify(fakeManager, times(1)).canUseFullScreenIntent();
+ }
+ }
+
public void testGetActiveNotifications() {
NotificationManager fakeManager = mock(NotificationManager.class);
NotificationManagerCompat notificationManager =
diff --git a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
index c1e0eda..a0ce095 100644
--- a/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/ContextCompatTest.java
@@ -87,6 +87,7 @@
import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.SearchManager;
+import android.app.UiAutomation;
import android.app.UiModeManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
@@ -531,11 +532,15 @@
@Test
@SdkSuppress(minSdkVersion = 29, maxSdkVersion = 32)
public void testRegisterReceiverPermissionNotGrantedApi26() {
- InstrumentationRegistry
- .getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
- assertThrows(RuntimeException.class,
- () -> ContextCompat.registerReceiver(mContext,
- mTestReceiver, mTestFilter, ContextCompat.RECEIVER_NOT_EXPORTED));
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ uiAutomation.adoptShellPermissionIdentity();
+ try {
+ assertThrows(RuntimeException.class,
+ () -> ContextCompat.registerReceiver(mContext,
+ mTestReceiver, mTestFilter, ContextCompat.RECEIVER_NOT_EXPORTED));
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
}
@Test
diff --git a/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java b/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java
new file mode 100644
index 0000000..a78f703
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.service.quicksettings;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.quicksettings.TileService;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test for {@link TileServiceCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TileServiceCompatTest {
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @After
+ public void tearDown() {
+ TileServiceCompat.clearTileServiceWrapper();
+ }
+
+ @SdkSuppress(minSdkVersion = 34)
+ @Test
+ public void startActivityAndCollapse_usesPendingIntent() {
+ TileServiceCompat.TileServiceWrapper tileServiceWrapper =
+ mock(TileServiceCompat.TileServiceWrapper.class);
+ TileService tileService = mock(TileService.class);
+ int requestCode = 7465;
+ Intent intent = new Intent();
+ Bundle options = new Bundle();
+ PendingIntentActivityWrapper wrapper = new PendingIntentActivityWrapper(mContext,
+ requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT, options, /* isMutable = */
+ true);
+ TileServiceCompat.setTileServiceWrapper(tileServiceWrapper);
+
+ TileServiceCompat.startActivityAndCollapse(tileService, wrapper);
+
+ verify(tileServiceWrapper).startActivityAndCollapse(wrapper.getPendingIntent());
+ }
+
+ @SdkSuppress(minSdkVersion = 24, maxSdkVersion = 33)
+ @Test
+ public void startActivityAndCollapse_usesIntent() {
+ TileServiceCompat.TileServiceWrapper tileServiceWrapper =
+ mock(TileServiceCompat.TileServiceWrapper.class);
+ TileService tileService = mock(TileService.class);
+ int requestCode = 7465;
+ Intent intent = new Intent();
+ Bundle options = new Bundle();
+ PendingIntentActivityWrapper wrapper = new PendingIntentActivityWrapper(mContext,
+ requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT, options, /* isMutable = */
+ true);
+ TileServiceCompat.setTileServiceWrapper(tileServiceWrapper);
+
+ TileServiceCompat.startActivityAndCollapse(tileService, wrapper);
+
+ verify(tileServiceWrapper).startActivityAndCollapse(intent);
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/text/util/LocalePreferencesTest.java b/core/core/src/androidTest/java/androidx/core/text/util/LocalePreferencesTest.java
new file mode 100644
index 0000000..a47cc38
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/text/util/LocalePreferencesTest.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.text.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Build.VERSION_CODES;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@SdkSuppress(minSdkVersion = VERSION_CODES.N)
+@RunWith(AndroidJUnit4.class)
+public class LocalePreferencesTest {
+ private static Locale sLocale;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ sLocale = Locale.getDefault(Locale.Category.FORMAT);
+ }
+
+ @After
+ public void tearDown() {
+ Locale.setDefault(sLocale);
+ }
+
+ // Hour cycle
+ @Test
+ public void getHourCycle_hasSubTags_resultIsH24() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getHourCycle();
+
+ assertEquals(LocalePreferences.HourCycle.H24, result);
+ }
+
+ @Test
+ public void getHourCycle_hasSubTagsWithoutHourCycleTag_resultIsH12() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getHourCycle();
+
+ assertEquals(LocalePreferences.HourCycle.H12, result);
+ }
+
+ @Test
+ public void getHourCycle_hasSubTagsAndDisableResolved_resultIsH24() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getHourCycle(false);
+
+ assertEquals(LocalePreferences.HourCycle.H24, result);
+ }
+
+ @Test
+ public void getHourCycle_hasSubTagsWithoutHourCycleTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getHourCycle(false);
+
+ assertEquals(LocalePreferences.HourCycle.DEFAULT, result);
+ }
+
+ @Test
+ public void getHourCycle_inputLocaleWithHourCycleTag_resultIsH12() throws Exception {
+ String result = LocalePreferences.getHourCycle(Locale.forLanguageTag("en-US-u-hc-h12"));
+
+ assertEquals(LocalePreferences.HourCycle.H12, result);
+ }
+
+ @Test
+ public void getHourCycle_inputLocaleWithoutHourCycleTag_resultIsH12() throws Exception {
+ String result = LocalePreferences.getHourCycle(Locale.forLanguageTag("en-US"));
+
+ assertEquals(LocalePreferences.HourCycle.H12, result);
+ }
+
+ @Test
+ public void getHourCycle_inputH23Locale_resultIsH23() throws Exception {
+ String result = LocalePreferences.getHourCycle(Locale.forLanguageTag("fr-FR"));
+
+ assertEquals(LocalePreferences.HourCycle.H23, result);
+ }
+
+ @Test
+ public void getHourCycle_inputH23LocaleWithHourCycleTag_resultIsH12() throws Exception {
+ String result = LocalePreferences.getHourCycle(Locale.forLanguageTag("fr-FR-u-hc-h12"));
+
+ assertEquals(LocalePreferences.HourCycle.H12, result);
+ }
+
+ @Test
+ public void getHourCycle_inputLocaleWithoutHourCycleTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ String result = LocalePreferences.getHourCycle(Locale.forLanguageTag("en-US"), false);
+
+ assertEquals(LocalePreferences.HourCycle.DEFAULT, result);
+ }
+
+ @Test
+ public void getHourCycle_compareHasResolvedValueIsTrueAndWithoutResolvedValue_sameResult()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("zh-TW-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ // Has Hour Cycle subtag
+ String resultWithoutResolvedValue = LocalePreferences.getHourCycle();
+ String resultResolvedIsTrue = LocalePreferences.getHourCycle(true);
+ assertEquals(resultWithoutResolvedValue, resultResolvedIsTrue);
+
+ // Does not have HourCycle subtag
+ Locale.setDefault(Locale.forLanguageTag("zh-TW-u-ca-chinese-mu-celsius-fw-wed"));
+
+ resultWithoutResolvedValue = LocalePreferences.getHourCycle();
+ resultResolvedIsTrue = LocalePreferences.getHourCycle(true);
+ assertEquals(resultWithoutResolvedValue, resultResolvedIsTrue);
+ }
+
+ // Calendar
+ @Test
+ public void getCalendarType_hasSubTags_resultIsChinese() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getCalendarType();
+
+ assertEquals(LocalePreferences.CalendarType.CHINESE, result);
+ }
+
+ @Test
+ public void getCalendarType_hasSubTagsWithoutCalendarTag_resultIsGregorian() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getCalendarType();
+
+ assertEquals(LocalePreferences.CalendarType.GREGORIAN, result);
+ }
+
+ @Test
+ public void getCalendarType_hasSubTagsAndDisableResolved_resultIsChinese() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getCalendarType(false);
+
+ assertEquals(LocalePreferences.CalendarType.CHINESE, result);
+ }
+
+ @Test
+ public void getCalendarType_hasSubTagsWithoutCalendarTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getCalendarType(false);
+
+ assertEquals(LocalePreferences.CalendarType.DEFAULT, result);
+ }
+
+ @Test
+ public void getCalendarType_inputLocaleWithCalendarTag_resultIsChinese() throws Exception {
+ String result =
+ LocalePreferences.getCalendarType(Locale.forLanguageTag("en-US-u-ca-chinese"));
+
+ assertEquals(LocalePreferences.CalendarType.CHINESE, result);
+ }
+
+ @Test
+ public void getCalendarType_inputLocaleWithoutCalendarTag_resultIsGregorian() throws Exception {
+ String result = LocalePreferences.getCalendarType(Locale.forLanguageTag("en-US"));
+
+ assertEquals(LocalePreferences.CalendarType.GREGORIAN, result);
+ }
+
+ @Test
+ public void getCalendarType_inputLocaleWithoutCalendarTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ String result = LocalePreferences.getCalendarType(Locale.forLanguageTag("en-US"), false);
+
+ assertEquals(LocalePreferences.CalendarType.DEFAULT, result);
+ }
+
+ // Temperature unit
+ @Test
+ public void getTemperatureUnit_hasSubTags_resultIsCelsius() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getTemperatureUnit();
+
+ assertEquals(LocalePreferences.TemperatureUnit.CELSIUS, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_hasSubTagsWithoutUnitTag_resultIsFahrenheit() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-hc-h24-fw-wed"));
+
+ String result = LocalePreferences.getTemperatureUnit();
+
+ assertEquals(LocalePreferences.TemperatureUnit.FAHRENHEIT, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_hasSubTagsAndDisableResolved_resultIsCelsius() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getTemperatureUnit(false);
+
+ assertEquals(LocalePreferences.TemperatureUnit.CELSIUS, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_hasSubTagsAndDisableResolved_resultIsFahrenheit()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("zh-TW-u-ca-chinese-hc-h24-mu-fahrenhe-fw-wed"));
+
+ String result = LocalePreferences.getTemperatureUnit(false);
+
+ assertEquals(LocalePreferences.TemperatureUnit.FAHRENHEIT, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_hasSubTagsWithoutUnitTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-fw-wed"));
+
+ String result = LocalePreferences.getTemperatureUnit(false);
+
+ assertEquals(LocalePreferences.TemperatureUnit.DEFAULT, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_inputLocaleWithUnitTag_resultIsCelsius() throws Exception {
+ String result = LocalePreferences
+ .getTemperatureUnit(Locale.forLanguageTag("en-US-u-mu-celsius"));
+
+ assertEquals(LocalePreferences.TemperatureUnit.CELSIUS, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_inputLocaleWithoutUnitTag_resultIsFahrenheit() throws Exception {
+ String result = LocalePreferences.getTemperatureUnit(Locale.forLanguageTag("en-US"));
+
+ assertEquals(LocalePreferences.TemperatureUnit.FAHRENHEIT, result);
+ }
+
+ @Test
+ public void getTemperatureUnit_inputLocaleWithoutUnitTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ String result = LocalePreferences
+ .getTemperatureUnit(Locale.forLanguageTag("en-US"), false);
+
+ assertEquals(LocalePreferences.TemperatureUnit.DEFAULT, result);
+ }
+
+ // First day of week
+ @Test
+ public void getFirstDayOfWeek_hasSubTags_resultIsCelsius() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getFirstDayOfWeek();
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.WEDNESDAY, result);
+ }
+
+ @Test
+ public void getFirstDayOfWeek_hasSubTagsWithoutFwTag_resultIsSun() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-hc-h24"));
+
+ String result = LocalePreferences.getFirstDayOfWeek();
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.SUNDAY, result);
+
+ }
+
+ @Test
+ public void getFirstDayOfWeek_hasSubTagsAndDisableResolved_resultIsWed() throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese-hc-h24-mu-celsius-fw-wed"));
+
+ String result = LocalePreferences.getFirstDayOfWeek(false);
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.WEDNESDAY, result);
+ }
+
+ @Test
+ public void getFirstDayOfWeek_hasSubTagsWithoutFwTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ Locale.setDefault(Locale.forLanguageTag("en-US-u-ca-chinese"));
+
+ String result = LocalePreferences.getFirstDayOfWeek(false);
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.DEFAULT, result);
+ }
+
+ @Test
+ public void getFirstDayOfWeek_inputLocaleWithFwTag_resultIsWed() throws Exception {
+ String result = LocalePreferences
+ .getFirstDayOfWeek(Locale.forLanguageTag("en-US-u-fw-wed"));
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.WEDNESDAY, result);
+ }
+
+ @Test
+ public void getFirstDayOfWeek_inputLocaleWithoutFwTag_resultIsSun() throws Exception {
+ String result = LocalePreferences.getFirstDayOfWeek(Locale.forLanguageTag("en-US"));
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.SUNDAY, result);
+ }
+
+ @Test
+ public void getFirstDayOfWeek_inputLocaleWithoutFwTagAndDisableResolved_resultIsEmpty()
+ throws Exception {
+ String result = LocalePreferences
+ .getFirstDayOfWeek(Locale.forLanguageTag("en-US"), false);
+
+ assertEquals(LocalePreferences.FirstDayOfWeek.DEFAULT, result);
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/util/TypedValueCompatTest.kt b/core/core/src/androidTest/java/androidx/core/util/TypedValueCompatTest.kt
new file mode 100644
index 0000000..8ad6646
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/util/TypedValueCompatTest.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.core.util
+
+import android.util.DisplayMetrics
+import android.util.TypedValue
+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 com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TypedValueCompatTest {
+ @Test
+ fun invalidUnitThrows() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ val fontScale = 2f
+ metrics.density = 1f
+ metrics.xdpi = 2f
+ metrics.scaledDensity = fontScale * metrics.density
+
+ assertThrows(IllegalArgumentException::class.java) {
+ TypedValueCompat.deriveDimension(TypedValue.COMPLEX_UNIT_MM + 1, 23f, metrics)
+ }
+ }
+
+ @Test
+ fun density0_deriveDoesNotCrash() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ metrics.density = 0f
+ metrics.xdpi = 0f
+ metrics.scaledDensity = 0f
+
+ listOf(
+ TypedValue.COMPLEX_UNIT_DIP,
+ TypedValue.COMPLEX_UNIT_SP,
+ TypedValue.COMPLEX_UNIT_PT,
+ TypedValue.COMPLEX_UNIT_IN,
+ TypedValue.COMPLEX_UNIT_MM
+ )
+ .forEach { dimenType ->
+ assertThat(TypedValueCompat.deriveDimension(dimenType, 23f, metrics))
+ .isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun scaledDensity0_deriveSpDoesNotCrash() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ metrics.density = 1f
+ metrics.xdpi = 2f
+ metrics.scaledDensity = 0f
+
+ assertThat(TypedValueCompat.deriveDimension(TypedValue.COMPLEX_UNIT_SP, 23f, metrics))
+ .isEqualTo(0)
+ }
+
+ @SdkSuppress(minSdkVersion = 34)
+ @Test
+ fun deriveDimensionMatchesRealVersion() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ metrics.density = 1f
+ metrics.xdpi = 2f
+ metrics.scaledDensity = 2f
+
+ listOf(
+ TypedValue.COMPLEX_UNIT_PX,
+ TypedValue.COMPLEX_UNIT_DIP,
+ TypedValue.COMPLEX_UNIT_SP,
+ TypedValue.COMPLEX_UNIT_PT,
+ TypedValue.COMPLEX_UNIT_IN,
+ TypedValue.COMPLEX_UNIT_MM
+ )
+ .forEach { dimenType ->
+ for (i: Int in -1000 until 1000) {
+ assertThat(TypedValueCompat.deriveDimension(dimenType, i.toFloat(), metrics))
+ .isWithin(0.05f)
+ .of(TypedValue.deriveDimension(dimenType, i.toFloat(), metrics))
+ }
+ }
+ }
+
+ @Test
+ fun eachUnitType_roundTripIsEqual() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ metrics.density = 1f
+ metrics.xdpi = 2f
+ metrics.scaledDensity = 2f
+
+ listOf(
+ TypedValue.COMPLEX_UNIT_PX,
+ TypedValue.COMPLEX_UNIT_DIP,
+ TypedValue.COMPLEX_UNIT_SP,
+ TypedValue.COMPLEX_UNIT_PT,
+ TypedValue.COMPLEX_UNIT_IN,
+ TypedValue.COMPLEX_UNIT_MM
+ )
+ .forEach { dimenType ->
+ for (i: Int in -10000 until 10000) {
+ assertRoundTripIsEqual(i.toFloat(), dimenType, metrics)
+ assertRoundTripIsEqual(i - .1f, dimenType, metrics)
+ assertRoundTripIsEqual(i + .5f, dimenType, metrics)
+ }
+ }
+ }
+
+ @Test
+ fun convenienceFunctionsCallCorrectAliases() {
+ val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+ metrics.density = 1f
+ metrics.xdpi = 2f
+ metrics.scaledDensity = 2f
+
+ assertThat(TypedValueCompat.pxToDp(20f, metrics))
+ .isWithin(0.05f)
+ .of(TypedValueCompat.deriveDimension(TypedValue.COMPLEX_UNIT_DIP, 20f, metrics))
+ assertThat(TypedValueCompat.pxToSp(20f, metrics))
+ .isWithin(0.05f)
+ .of(TypedValueCompat.deriveDimension(TypedValue.COMPLEX_UNIT_SP, 20f, metrics))
+ assertThat(TypedValueCompat.dpToPx(20f, metrics))
+ .isWithin(0.05f)
+ .of(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20f, metrics))
+ assertThat(TypedValueCompat.spToPx(20f, metrics))
+ .isWithin(0.05f)
+ .of(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20f, metrics))
+ }
+
+ private fun assertRoundTripIsEqual(
+ dimenValueToTest: Float,
+ dimenType: Int,
+ metrics: DisplayMetrics,
+ ) {
+ val actualPx = TypedValue.applyDimension(dimenType, dimenValueToTest, metrics)
+ val actualDimenValue = TypedValueCompat.deriveDimension(dimenType, actualPx, metrics)
+ assertWithMessage(
+ "TypedValue.applyDimension for type %s on %s = %s should equal " +
+ "TypedValueCompat.deriveDimension of %s",
+ dimenType,
+ dimenValueToTest,
+ actualPx,
+ actualDimenValue
+ )
+ .that(dimenValueToTest)
+ .isWithin(0.05f)
+ .of(actualDimenValue)
+ }
+}
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/view/VelocityTrackerCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/VelocityTrackerCompatTest.java
new file mode 100644
index 0000000..bb8d29a
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/VelocityTrackerCompatTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view;
+
+import static android.view.MotionEvent.AXIS_BRAKE;
+import static android.view.MotionEvent.AXIS_X;
+import static android.view.MotionEvent.AXIS_Y;
+
+import static androidx.core.view.MotionEventCompat.AXIS_SCROLL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VelocityTrackerCompatTest {
+ /** Arbitrarily chosen velocities across different supported dimensions and some pointer IDs. */
+ private static final float X_VEL_POINTER_ID_1 = 5;
+ private static final float X_VEL_POINTER_ID_2 = 6;
+ private static final float Y_VEL_POINTER_ID_1 = 7;
+ private static final float Y_VEL_POINTER_ID_2 = 8;
+ private static final float SCROLL_VEL_POINTER_ID_1 = 9;
+ private static final float SCROLL_VEL_POINTER_ID_2 = 10;
+
+ /**
+ * A small enough step time stamp (ms), that the VelocityTracker wouldn't consider big enough to
+ * assume a pointer has stopped.
+ */
+ private static final long TIME_STEP_MS = 10;
+
+ /**
+ * An arbitrarily chosen value for the number of times a movement particular type of movement
+ * is added to a tracker. For velocities to be non-zero, we should generally have 2/3 movements,
+ * so 4 is a good value to use.
+ */
+ private static final int NUM_MOVEMENTS = 4;
+
+ private VelocityTracker mPlanarTracker;
+ private VelocityTracker mScrollTracker;
+
+ @Before
+ public void setup() {
+ mPlanarTracker = VelocityTracker.obtain();
+ mScrollTracker = VelocityTracker.obtain();
+
+ long time = 0;
+ float xPointer1 = 0;
+ float yPointer1 = 0;
+ float scrollPointer1 = 0;
+ float xPointer2 = 0;
+ float yPointer2 = 0;
+ float scrollPointer2 = 0;
+
+ // Add MotionEvents to create some velocity!
+ // Note that: the goal of these tests is not to check the specific values of the velocities,
+ // but instead, compare the outputs of the Compat tracker against the platform tracker.
+ for (int i = 0; i < NUM_MOVEMENTS; i++) {
+ time += TIME_STEP_MS;
+ xPointer1 += X_VEL_POINTER_ID_1 * TIME_STEP_MS;
+ yPointer1 += Y_VEL_POINTER_ID_1 * TIME_STEP_MS;
+ scrollPointer1 = SCROLL_VEL_POINTER_ID_1 * TIME_STEP_MS;
+
+ xPointer2 += X_VEL_POINTER_ID_2 * TIME_STEP_MS;
+ yPointer2 += Y_VEL_POINTER_ID_2 * TIME_STEP_MS;
+ scrollPointer2 = SCROLL_VEL_POINTER_ID_2 * TIME_STEP_MS;
+
+ addPlanarMotionEvent(1, time, xPointer1, yPointer1);
+ addPlanarMotionEvent(2, time, xPointer2, yPointer2);
+ addScrollMotionEvent(1, time, scrollPointer1);
+ addScrollMotionEvent(2, time, scrollPointer2);
+ }
+
+ mPlanarTracker.computeCurrentVelocity(1000);
+ mScrollTracker.computeCurrentVelocity(1000);
+ }
+
+ @Test
+ public void testIsAxisSupported_planarAxes() {
+ assertTrue(VelocityTrackerCompat.isAxisSupported(VelocityTracker.obtain(), AXIS_X));
+ assertTrue(VelocityTrackerCompat.isAxisSupported(VelocityTracker.obtain(), AXIS_Y));
+ }
+
+ @Test
+ public void testIsAxisSupported_nonPlanarAxes() {
+ if (Build.VERSION.SDK_INT >= 34) {
+ assertTrue(
+ VelocityTrackerCompat.isAxisSupported(VelocityTracker.obtain(), AXIS_SCROLL));
+ } else {
+ assertFalse(
+ VelocityTrackerCompat.isAxisSupported(VelocityTracker.obtain(), AXIS_SCROLL));
+ }
+
+ // Check against an axis that has not yet been supported at any Android version.
+ assertFalse(VelocityTrackerCompat.isAxisSupported(VelocityTracker.obtain(), AXIS_BRAKE));
+ }
+
+ @Test
+ public void testGetAxisVelocity_planarAxes_noPointerId_againstEquivalentPlatformApis() {
+ if (Build.VERSION.SDK_INT >= 34) {
+ float compatXVelocity = VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_X);
+ float compatYVelocity = VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_Y);
+
+ assertEquals(mPlanarTracker.getAxisVelocity(AXIS_X), compatXVelocity, 0);
+ assertEquals(mPlanarTracker.getAxisVelocity(AXIS_Y), compatYVelocity, 0);
+ }
+ }
+
+ @Test
+ public void testGetAxisVelocity_planarAxes_withPointerId_againstEquivalentPlatformApis() {
+ if (Build.VERSION.SDK_INT >= 34) {
+ float compatXVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_X, 2);
+ float compatYVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_Y, 2);
+
+ assertEquals(mPlanarTracker.getAxisVelocity(AXIS_X, 2), compatXVelocity, 0);
+ assertEquals(mPlanarTracker.getAxisVelocity(AXIS_Y, 2), compatYVelocity, 0);
+ }
+ }
+
+ @Test
+ public void testGetAxisVelocity_planarAxes_noPointerId_againstGenericXAndYVelocityApis() {
+ float compatXVelocity = VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_X);
+ float compatYVelocity = VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_Y);
+
+ assertEquals(mPlanarTracker.getXVelocity(), compatXVelocity, 0);
+ assertEquals(mPlanarTracker.getYVelocity(), compatYVelocity, 0);
+ }
+
+ @Test
+ public void testGetAxisVelocity_planarAxes_withPointerId_againstGenericXAndYVelocityApis() {
+ float compatXVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_X, 2);
+ float compatYVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mPlanarTracker, AXIS_Y, 2);
+
+ assertEquals(mPlanarTracker.getXVelocity(2), compatXVelocity, 0);
+ assertEquals(mPlanarTracker.getYVelocity(2), compatYVelocity, 0);
+ }
+
+ @Test
+ public void testGetAxisVelocity_axisScroll_noPointerId() {
+ float compatScrollVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mScrollTracker, AXIS_SCROLL);
+
+ if (Build.VERSION.SDK_INT >= 34) {
+ assertEquals(mScrollTracker.getAxisVelocity(AXIS_SCROLL), compatScrollVelocity, 0);
+ } else {
+ assertEquals(0, compatScrollVelocity, 0);
+ }
+ }
+
+ @Test
+ public void testGetAxisVelocity_axisScroll_withPointerId() {
+ float compatScrollVelocity =
+ VelocityTrackerCompat.getAxisVelocity(mScrollTracker, AXIS_SCROLL, 2);
+
+ if (Build.VERSION.SDK_INT >= 34) {
+ assertEquals(mScrollTracker.getAxisVelocity(AXIS_SCROLL, 2), compatScrollVelocity, 0);
+ } else {
+ assertEquals(0, compatScrollVelocity, 0);
+ }
+ }
+
+
+ private void addPlanarMotionEvent(int pointerId, long time, float x, float y) {
+ MotionEvent ev = MotionEvent.obtain(0L, time, MotionEvent.ACTION_MOVE, x, y, 0);
+ mPlanarTracker.addMovement(ev);
+ ev.recycle();
+ }
+ private void addScrollMotionEvent(int pointerId, long time, float scrollAmount) {
+ MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
+ props.id = pointerId;
+
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAmount);
+
+ MotionEvent ev = MotionEvent.obtain(0 /* downTime */,
+ time,
+ MotionEvent.ACTION_SCROLL,
+ 1 /* pointerCount */,
+ new MotionEvent.PointerProperties[] {props},
+ new MotionEvent.PointerCoords[] {coords},
+ 0 /* metaState */,
+ 0 /* buttonState */,
+ 0 /* xPrecision */,
+ 0 /* yPrecision */,
+ 1 /* deviceId */,
+ 0 /* edgeFlags */,
+ InputDevice.SOURCE_ROTARY_ENCODER,
+ 0 /* flags */);
+ mScrollTracker.addMovement(ev);
+ ev.recycle();
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/ViewConfigurationCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/ViewConfigurationCompatTest.java
new file mode 100644
index 0000000..3828f1f
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/ViewConfigurationCompatTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view;
+
+import static android.view.MotionEvent.AXIS_X;
+import static android.view.MotionEvent.AXIS_Y;
+
+import static androidx.core.view.InputDeviceCompat.SOURCE_ROTARY_ENCODER;
+import static androidx.core.view.InputDeviceCompat.SOURCE_TOUCHSCREEN;
+import static androidx.core.view.MotionEventCompat.AXIS_SCROLL;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.input.InputManager;
+import android.view.InputDevice;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewConfigurationCompatTest {
+ @Mock Resources mResourcesMock;
+ @Mock ViewConfiguration mViewConfigMock;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledFlingThresholds_withDeviceParams_apiPre34() {
+ InputDevice device = findInputDevice(SOURCE_TOUCHSCREEN);
+ if (device == null) {
+ return;
+ }
+ when(mViewConfigMock.getScaledMinimumFlingVelocity()).thenReturn(10);
+ when(mViewConfigMock.getScaledMaximumFlingVelocity()).thenReturn(20);
+
+ assertFlingThresholds(
+ mViewConfigMock,
+ device.getId(),
+ AXIS_X,
+ SOURCE_TOUCHSCREEN,
+ /* minVel= */ 10,
+ /* maxVel= */ 20);
+ assertFlingThresholds(
+ mViewConfigMock,
+ device.getId(),
+ AXIS_Y,
+ SOURCE_TOUCHSCREEN,
+ /* minVel= */ 10,
+ /* maxVel= */ 20);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledFlingThresholds_realTouchScreenDevice_apiPre34() {
+ InputDevice touchScreenDevice = findInputDevice(SOURCE_TOUCHSCREEN);
+ if (touchScreenDevice == null) {
+ return;
+ }
+ ViewConfiguration vc = ViewConfiguration.get(mContext);
+
+ assertFlingThresholds(
+ vc,
+ touchScreenDevice.getId(),
+ AXIS_X,
+ SOURCE_TOUCHSCREEN,
+ vc.getScaledMinimumFlingVelocity(),
+ vc.getScaledMaximumFlingVelocity());
+ assertFlingThresholds(
+ vc,
+ touchScreenDevice.getId(),
+ AXIS_Y,
+ SOURCE_TOUCHSCREEN,
+ vc.getScaledMinimumFlingVelocity(),
+ vc.getScaledMaximumFlingVelocity());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledFlingThresholds_realRotaryEncoderDevice_hasNoAndroidResForFling_apiPre34() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+ when(mViewConfigMock.getScaledMinimumFlingVelocity()).thenReturn(10);
+ when(mViewConfigMock.getScaledMaximumFlingVelocity()).thenReturn(20);
+ mockAndroidResource("config_viewMinRotaryEncoderFlingVelocity", "dimen", /* resId= */ 0);
+ mContext = spy(mContext);
+ when(mContext.getResources()).thenReturn(mResourcesMock);
+
+ assertFlingThresholds(
+ mViewConfigMock,
+ rotaryEncoderDevice.getId(),
+ AXIS_SCROLL,
+ SOURCE_ROTARY_ENCODER,
+ /* minVel= */ 10,
+ /* maxVel= */ 20);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledMinFlingVelocity_realRotaryEncoderDevice_hasAndroidResForFling_apiPre34() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+ int deviceId = rotaryEncoderDevice.getId();
+ mockAndroidResource("config_viewMinRotaryEncoderFlingVelocity", "dimen", /* resId= */ 1);
+ when(mResourcesMock.getDimensionPixelSize(1)).thenReturn(100);
+ mContext = spy(mContext);
+ when(mContext.getResources()).thenReturn(mResourcesMock);
+
+ assertEquals(
+ 100,
+ ViewConfigurationCompat.getScaledMinimumFlingVelocity(
+ mContext, mViewConfigMock, deviceId, AXIS_SCROLL, SOURCE_ROTARY_ENCODER));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledMinFlingVelocity_realRotaryEncoderDevice_hasAndroidResForNoFling_apiPre34() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+ int deviceId = rotaryEncoderDevice.getId();
+ mockAndroidResource("config_viewMinRotaryEncoderFlingVelocity", "dimen", /* resId= */ 1);
+ when(mResourcesMock.getDimensionPixelSize(1)).thenReturn(-1);
+ mContext = spy(mContext);
+ when(mContext.getResources()).thenReturn(mResourcesMock);
+
+ assertEquals(
+ Integer.MAX_VALUE,
+ ViewConfigurationCompat.getScaledMinimumFlingVelocity(
+ mContext, mViewConfigMock, deviceId, AXIS_SCROLL, SOURCE_ROTARY_ENCODER));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledMaxFlingVelocity_hasAndroidResForFling_realRotaryEncoderDevice_apiPre34() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+ int deviceId = rotaryEncoderDevice.getId();
+ mockAndroidResource("config_viewMaxRotaryEncoderFlingVelocity", "dimen", /* resId= */ 1);
+ when(mResourcesMock.getDimensionPixelSize(1)).thenReturn(100);
+ mContext = spy(mContext);
+ when(mContext.getResources()).thenReturn(mResourcesMock);
+
+ assertEquals(
+ 100,
+ ViewConfigurationCompat.getScaledMaximumFlingVelocity(
+ mContext, mViewConfigMock, deviceId, AXIS_SCROLL, SOURCE_ROTARY_ENCODER));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledMaxFlingVelocity_hasAndroidResForNoFling_realRotaryEncoderDevice_apiPre34() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+ int deviceId = rotaryEncoderDevice.getId();
+ mockAndroidResource("config_viewMaxRotaryEncoderFlingVelocity", "dimen", 1);
+ when(mResourcesMock.getDimensionPixelSize(1)).thenReturn(-1);
+ mContext = spy(mContext);
+ when(mContext.getResources()).thenReturn(mResourcesMock);
+
+ assertEquals(
+ Integer.MIN_VALUE,
+ ViewConfigurationCompat.getScaledMaximumFlingVelocity(
+ mContext, mViewConfigMock, deviceId, AXIS_SCROLL, SOURCE_ROTARY_ENCODER));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 16, maxSdkVersion = 33)
+ public void scaledFlingThresholds_invalidInputDeviceParameters_apiPre34() {
+ assertFlingThresholds(
+ mViewConfigMock,
+ -10, // Bad InputDevice ID.
+ AXIS_X,
+ SOURCE_TOUCHSCREEN,
+ Integer.MAX_VALUE,
+ Integer.MIN_VALUE);
+
+ InputDevice touchScreenDevice = findInputDevice(SOURCE_TOUCHSCREEN);
+ if (touchScreenDevice == null) {
+ return;
+ }
+
+ assertFlingThresholds(
+ mViewConfigMock,
+ touchScreenDevice.getId(),
+ /* axis= */ -1, // Axis cannot be a negative value.
+ SOURCE_TOUCHSCREEN,
+ Integer.MAX_VALUE,
+ Integer.MIN_VALUE);
+ assertFlingThresholds(
+ mViewConfigMock,
+ touchScreenDevice.getId(),
+ AXIS_SCROLL, // Touch does not report on AXIS_SCROLL.
+ SOURCE_TOUCHSCREEN,
+ Integer.MAX_VALUE,
+ Integer.MIN_VALUE);
+ assertFlingThresholds(
+ mViewConfigMock,
+ touchScreenDevice.getId(),
+ AXIS_X,
+ /* source = */ -1, // Source cannot be a negative value.
+ Integer.MAX_VALUE,
+ Integer.MIN_VALUE);
+ assertFlingThresholds(
+ mViewConfigMock,
+ touchScreenDevice.getId(),
+ AXIS_X,
+ SOURCE_ROTARY_ENCODER, // Touch does not have rotary encoder source.
+ Integer.MAX_VALUE,
+ Integer.MIN_VALUE);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 34)
+ public void scaledVelocityThresholds_withDeviceParams_api34Plus() {
+ when(mViewConfigMock.getScaledMinimumFlingVelocity(1, 2, 3)).thenReturn(100);
+ when(mViewConfigMock.getScaledMaximumFlingVelocity(1, 2, 3)).thenReturn(200);
+
+ assertEquals(
+ 100,
+ ViewConfigurationCompat.getScaledMinimumFlingVelocity(
+ mContext, mViewConfigMock, 1, 2, 3));
+ assertEquals(
+ 200,
+ ViewConfigurationCompat.getScaledMaximumFlingVelocity(
+ mContext, mViewConfigMock, 1, 2, 3));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 34)
+ public void scaledFlingThresholds_realTouchScreenDevice_api34Plus() {
+ InputDevice touchScreenDevice = findInputDevice(SOURCE_TOUCHSCREEN);
+ if (touchScreenDevice == null) {
+ return;
+ }
+
+ assertFlingThresholdsEqualPlatformImpl(
+ ViewConfiguration.get(mContext),
+ touchScreenDevice.getId(),
+ SOURCE_TOUCHSCREEN,
+ AXIS_X);
+ assertFlingThresholdsEqualPlatformImpl(
+ ViewConfiguration.get(mContext),
+ touchScreenDevice.getId(),
+ SOURCE_TOUCHSCREEN,
+ AXIS_Y);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 34)
+ public void scaledFlingThresholds_realRotaryEncoderDevice_api34Plus() {
+ InputDevice rotaryEncoderDevice = findInputDevice(SOURCE_ROTARY_ENCODER);
+ if (rotaryEncoderDevice == null) {
+ return;
+ }
+
+ assertFlingThresholdsEqualPlatformImpl(
+ ViewConfiguration.get(mContext),
+ rotaryEncoderDevice.getId(),
+ SOURCE_ROTARY_ENCODER,
+ AXIS_SCROLL);
+ }
+
+ @SdkSuppress(minSdkVersion = 34)
+ private void assertFlingThresholdsEqualPlatformImpl(
+ ViewConfiguration vc, int inputDeviceId, int axis, int source) {
+ assertFlingThresholds(
+ vc,
+ inputDeviceId,
+ axis,
+ source,
+ vc.getScaledMinimumFlingVelocity(inputDeviceId, axis, source),
+ vc.getScaledMaximumFlingVelocity(inputDeviceId, axis, source));
+ }
+
+ private void assertFlingThresholds(
+ ViewConfiguration vc, int inputDeviceId, int axis, int source, int minVel, int maxVel) {
+ assertEquals(
+ minVel,
+ ViewConfigurationCompat.getScaledMinimumFlingVelocity(
+ mContext, vc, inputDeviceId, axis, source));
+ assertEquals(
+ maxVel,
+ ViewConfigurationCompat.getScaledMaximumFlingVelocity(
+ mContext, vc, inputDeviceId, axis, source));
+ }
+
+ @SdkSuppress(minSdkVersion = 16)
+ @Nullable
+ private InputDevice findInputDevice(int source) {
+ InputManager inputManager =
+ (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
+ int[] deviceIds = inputManager.getInputDeviceIds();
+ for (int deviceId : deviceIds) {
+ InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (inputDevice == null) {
+ continue;
+ }
+ List<InputDevice.MotionRange> motionRangeList = inputDevice.getMotionRanges();
+ for (InputDevice.MotionRange motionRange : motionRangeList) {
+ if (motionRange.getSource() == source) {
+ return inputDevice;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void mockAndroidResource(String name, String defType, int resId) {
+ when(mResourcesMock.getIdentifier(name, defType, /* defPackage= */ "android"))
+ .thenReturn(resId);
+ }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
index 805a399..3b7b2d8 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
@@ -119,6 +119,15 @@
@SdkSuppress(minSdkVersion = 19)
@Test
+ public void testisGranularScrollingSupported() {
+ AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
+ assertThat(nodeCompat.isGranularScrollingSupported(), is(false));
+ nodeCompat.setGranularScrollingSupported(true);
+ assertThat(nodeCompat.isGranularScrollingSupported(), is(true));
+ }
+
+ @SdkSuppress(minSdkVersion = 19)
+ @Test
public void testGetSetHeading() {
AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
nodeCompat.setHeading(true);
@@ -325,4 +334,16 @@
accessibilityNodeInfoCompat.setTextSelectable(true);
assertThat(accessibilityNodeInfoCompat.isTextSelectable(), equalTo(true));
}
+
+ @SdkSuppress(minSdkVersion = 34)
+ @SmallTest
+ @Test
+ public void testActionScrollInDirection() {
+ AccessibilityActionCompat actionCompat =
+ AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION;
+ assertThat(actionCompat.getId(),
+ is(getExpectedActionId(android.R.id.accessibilityActionScrollInDirection)));
+ assertThat(actionCompat.toString(), is("AccessibilityActionCompat: "
+ + "ACTION_SCROLL_IN_DIRECTION"));
+ }
}
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
index a1afdfda..1788e22 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
@@ -17,7 +17,9 @@
package androidx.core.view.accessibility;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNot.not;
import android.annotation.TargetApi;
import android.graphics.Region;
@@ -40,6 +42,17 @@
return AccessibilityWindowInfoCompat.wrapNonNullInstance(accessibilityWindowInfo);
}
+ @SdkSuppress(minSdkVersion = 30)
+ @SmallTest
+ @Test
+ public void testConstructor() {
+ AccessibilityWindowInfoCompat infoCompat = new AccessibilityWindowInfoCompat();
+ AccessibilityWindowInfo info = new AccessibilityWindowInfo();
+
+ assertThat(infoCompat.unwrap(), is(not(equalTo(null))));
+ assertThat(infoCompat.unwrap(), equalTo(info));
+ }
+
@SdkSuppress(minSdkVersion = 33)
@SmallTest
@Test
diff --git a/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java b/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java
new file mode 100644
index 0000000..d1f442b
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.app;
+
+import android.app.GrammaticalInflectionManager;
+import android.content.Context;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.DoNotInline;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.os.BuildCompat;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Helper for accessing features in {@link android.app.GrammaticalInflectionManager}.
+ */
+public final class GrammaticalInflectionManagerCompat {
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef(value = {
+ GRAMMATICAL_GENDER_NOT_SPECIFIED,
+ GRAMMATICAL_GENDER_NEUTRAL,
+ GRAMMATICAL_GENDER_FEMININE,
+ GRAMMATICAL_GENDER_MASCULINE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GrammaticalGender {}
+
+ /**
+ * Constant for grammatical gender: to indicate the user has not specified the terms
+ * of address for the application.
+ *
+ * @see android.content.res.Configuration#GRAMMATICAL_GENDER_NOT_SPECIFIED
+ */
+ public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0;
+
+ /**
+ * Constant for grammatical gender: to indicate the terms of address the user
+ * preferred in an application is neuter.
+ *
+ * @see android.content.res.Configuration#GRAMMATICAL_GENDER_NEUTRAL
+ */
+ public static final int GRAMMATICAL_GENDER_NEUTRAL = 1;
+
+ /**
+ * Constant for grammatical gender: to indicate the terms of address the user
+ * preferred in an application is feminine.
+ *
+ * @see android.content.res.Configuration#GRAMMATICAL_GENDER_FEMININE
+ */
+ public static final int GRAMMATICAL_GENDER_FEMININE = 2;
+
+ /**
+ * Constant for grammatical gender: to indicate the terms of address the user
+ * preferred in an application is masculine.
+ *
+ * @see android.content.res.Configuration#GRAMMATICAL_GENDER_MASCULINE
+ */
+ public static final int GRAMMATICAL_GENDER_MASCULINE = 3;
+
+ private GrammaticalInflectionManagerCompat() {}
+
+ /**
+ * Returns the current grammatical gender. No-op on versions prior to
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.
+ *
+ * @param context Context to retrieve service from.
+ * @return the grammatical gender if device API level is greater than 33, otherwise, return 0.
+ */
+ @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
+ @AnyThread
+ public static int getApplicationGrammaticalGender(@NonNull Context context) {
+ if (BuildCompat.isAtLeastU()) {
+ return Api34Impl.getApplicationGrammaticalGender(context);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the current grammatical gender. No-op on versions prior to
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.
+ *
+ * @param context Context to retrieve service from.
+ * @param grammaticalGender the terms of address the user preferred in an application.
+ */
+ @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
+ @AnyThread
+ public static void setRequestedApplicationGrammaticalGender(
+ @NonNull Context context, @GrammaticalGender int grammaticalGender) {
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.setRequestedApplicationGrammaticalGender(context, grammaticalGender);
+ }
+ }
+
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() {}
+
+ @DoNotInline
+ static int getApplicationGrammaticalGender(Context context) {
+ return getGrammaticalInflectionManager(context).getApplicationGrammaticalGender();
+ }
+
+ @DoNotInline
+ static void setRequestedApplicationGrammaticalGender(
+ Context context, int grammaticalGender) {
+ getGrammaticalInflectionManager(context)
+ .setRequestedApplicationGrammaticalGender(grammaticalGender);
+ }
+
+ private static GrammaticalInflectionManager getGrammaticalInflectionManager(
+ Context context) {
+ return context.getSystemService(GrammaticalInflectionManager.class);
+ }
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
index ae70ed1..48bf924 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
@@ -24,12 +24,14 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.os.Build;
@@ -808,6 +810,41 @@
}
/**
+ * Returns whether the calling app can send fullscreen intents.
+ *
+ * <p>Fullscreen intents were introduced in Android
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, where apps could always attach a full
+ * screen intent to their notification via
+ * {@link Notification.Builder#setFullScreenIntent(PendingIntent, boolean)}}.
+ *
+ * <p>Android {@link android.os.Build.VERSION_CODES#Q} introduced the
+ * {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}
+ * permission, where SystemUI will only show the full screen intent attached to a notification
+ * if the permission is declared in the manifest.
+ *
+ * <p>Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
+ * may not have permission to use {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}. If
+ * the FSI permission is denied, SystemUI will show the notification as an expanded heads up
+ * notification on lockscreen.
+ *
+ * <p>To request access, add the {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}
+ * permission to your manifest, and use
+ * {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT} to send the user
+ * to the settings page where they can grant your app the FSI permission.
+ */
+ public boolean canUseFullScreenIntent() {
+ if (Build.VERSION.SDK_INT < 29) {
+ return true;
+ }
+ if (Build.VERSION.SDK_INT < 34) {
+ final int permissionState =
+ mContext.checkSelfPermission(Manifest.permission.USE_FULL_SCREEN_INTENT);
+ return permissionState == PackageManager.PERMISSION_GRANTED;
+ }
+ return Api34Impl.canUseFullScreenIntent(mNotificationManager);
+ }
+
+ /**
* Returns true if this notification should use the side channel for delivery.
*/
private static boolean useSideChannelForNotification(Notification notification) {
@@ -1362,4 +1399,18 @@
}
}
+ /**
+ * A class for wrapping calls to {@link Notification.Builder} methods which
+ * were added in API 34; these calls must be wrapped to avoid performance issues.
+ * See the UnsafeNewApiCall lint rule for more details.
+ */
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() { }
+
+ @DoNotInline
+ static boolean canUseFullScreenIntent(NotificationManager notificationManager) {
+ return notificationManager.canUseFullScreenIntent();
+ }
+ }
}
diff --git a/core/core/src/main/java/androidx/core/app/ServiceCompat.java b/core/core/src/main/java/androidx/core/app/ServiceCompat.java
index 69d1a20..09d7b64 100644
--- a/core/core/src/main/java/androidx/core/app/ServiceCompat.java
+++ b/core/core/src/main/java/androidx/core/app/ServiceCompat.java
@@ -16,17 +16,23 @@
package androidx.core.app;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
+
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.app.Notification;
import android.app.Service;
+import android.content.pm.ServiceInfo;
import android.os.Build;
import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.core.os.BuildCompat;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -88,6 +94,92 @@
@Retention(RetentionPolicy.SOURCE)
public @interface StopForegroundFlags {}
+ private static final int FOREGROUND_SERVICE_TYPE_ALLOWED_SINCE_Q =
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+
+ private static final int FOREGROUND_SERVICE_TYPE_ALLOWED_SINCE_U =
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
+ | ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
+
+ /**
+ * {@link Service#startForeground(int, Notification, int)} with the third parameter
+ * {@code foregroundServiceType} was added in {@link android.os.Build.VERSION_CODES#Q}.
+ *
+ * <p>Before SDK Version {@link android.os.Build.VERSION_CODES#Q}, this method call should call
+ * {@link Service#startForeground(int, Notification)} without the {@code foregroundServiceType}
+ * parameter.</p>
+ *
+ * <p>Beginning with SDK Version {@link android.os.Build.VERSION_CODES#Q}, the allowed
+ * foregroundServiceType are:
+ * <ul>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE}</li>
+ * </ul>
+ * </p>
+ *
+ * <p>Beginning with SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} is not
+ * allowed to use {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE}. The allowed
+ * foregroundServiceType are:
+ * <ul>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}</li>
+ * <li>{@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}</li>
+ * </ul>
+ * </p>
+ *
+ * @see Service#startForeground(int, Notification)
+ * @see Service#startForeground(int, Notification, int)
+ */
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public static void startForeground(@NonNull Service service, int id,
+ @NonNull Notification notification, int foregroundServiceType) {
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.startForeground(service, id, notification, foregroundServiceType);
+ } else if (Build.VERSION.SDK_INT >= 29) {
+ Api29Impl.startForeground(service, id, notification, foregroundServiceType);
+ } else {
+ service.startForeground(id, notification);
+ }
+ }
+
/**
* Remove the passed service from foreground state, allowing it to be killed if
* more memory is needed.
@@ -115,4 +207,43 @@
service.stopForeground(flags);
}
}
+
+ @RequiresApi(29)
+ static class Api29Impl {
+ private Api29Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void startForeground(Service service, int id, Notification notification,
+ int foregroundServiceType) {
+ if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_NONE
+ || foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {
+ service.startForeground(id, notification, foregroundServiceType);
+ } else {
+ service.startForeground(id, notification,
+ foregroundServiceType & FOREGROUND_SERVICE_TYPE_ALLOWED_SINCE_Q);
+ }
+ }
+ }
+
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void startForeground(Service service, int id, Notification notification,
+ int foregroundServiceType) {
+ if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_NONE
+ || foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {
+ service.startForeground(id, notification, foregroundServiceType);
+ } else {
+ service.startForeground(id, notification,
+ foregroundServiceType & FOREGROUND_SERVICE_TYPE_ALLOWED_SINCE_U);
+ }
+ }
+ }
+
}
diff --git a/core/core/src/main/java/androidx/core/content/FileProvider.java b/core/core/src/main/java/androidx/core/content/FileProvider.java
index 40aa6ff..966425d 100644
--- a/core/core/src/main/java/androidx/core/content/FileProvider.java
+++ b/core/core/src/main/java/androidx/core/content/FileProvider.java
@@ -376,8 +376,12 @@
@GuardedBy("sCache")
private static final HashMap<String, PathStrategy> sCache = new HashMap<>();
- private PathStrategy mStrategy;
+ // Do not use {@code mLocalPathStrategy} directly; access it via {@link #getLocalPathStrategy}.
+ @GuardedBy("this")
+ @Nullable private PathStrategy mLocalPathStrategy;
+
private int mResourceId;
+ private String mAuthority;
public FileProvider() {
mResourceId = ResourcesCompat.ID_NULL;
@@ -416,12 +420,10 @@
throw new SecurityException("Provider must grant uri permissions");
}
- String authority = info.authority.split(";")[0];
+ mAuthority = info.authority.split(";")[0];
synchronized (sCache) {
- sCache.remove(authority);
+ sCache.remove(mAuthority);
}
-
- mStrategy = getPathStrategy(context, authority, mResourceId);
}
/**
@@ -514,7 +516,7 @@
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
// ContentProvider has already checked granted permissions
- final File file = mStrategy.getFileForUri(uri);
+ final File file = getLocalPathStrategy().getFileForUri(uri);
String displayName = uri.getQueryParameter(DISPLAYNAME_FIELD);
if (projection == null) {
@@ -555,7 +557,7 @@
@Override
public String getType(@NonNull Uri uri) {
// ContentProvider has already checked granted permissions
- final File file = mStrategy.getFileForUri(uri);
+ final File file = getLocalPathStrategy().getFileForUri(uri);
final int lastDot = file.getName().lastIndexOf('.');
if (lastDot >= 0) {
@@ -614,7 +616,7 @@
public int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs) {
// ContentProvider has already checked granted permissions
- final File file = mStrategy.getFileForUri(uri);
+ final File file = getLocalPathStrategy().getFileForUri(uri);
return file.delete() ? 1 : 0;
}
@@ -638,11 +640,22 @@
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
// ContentProvider has already checked granted permissions
- final File file = mStrategy.getFileForUri(uri);
+ final File file = getLocalPathStrategy().getFileForUri(uri);
final int fileMode = modeToMode(mode);
return ParcelFileDescriptor.open(file, fileMode);
}
+ /** Return the local {@link PathStrategy}, creating it if necessary. */
+ private PathStrategy getLocalPathStrategy() {
+ synchronized (this) {
+ if (mLocalPathStrategy == null) {
+ mLocalPathStrategy = getPathStrategy(getContext(), mAuthority, mResourceId);
+ }
+
+ return mLocalPathStrategy;
+ }
+ }
+
/**
* Return {@link PathStrategy} for given authority, either by parsing or
* returning from cache.
diff --git a/core/core/src/main/java/androidx/core/location/LocationCompat.java b/core/core/src/main/java/androidx/core/location/LocationCompat.java
index 6ccefa8..2e2c045 100644
--- a/core/core/src/main/java/androidx/core/location/LocationCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationCompat.java
@@ -81,7 +81,8 @@
@Nullable
private static Method sSetIsFromMockProviderMethod;
- private LocationCompat() {}
+ private LocationCompat() {
+ }
/**
* Return the time of this fix, in nanoseconds of elapsed real-time since system boot.
@@ -295,9 +296,17 @@
/**
* Returns the Mean Sea Level altitude of the location in meters.
*
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude does not exist. In
+ * order to allow for backwards compatibility and testing however, this method will attempt
+ * to read a double extra with the key {@link #EXTRA_MSL_ALTITUDE} and return the result.
+ *
* @throws IllegalStateException if the Mean Sea Level altitude of the location is not set
+ * @see Location#getMslAltitudeMeters()
*/
public static double getMslAltitudeMeters(@NonNull Location location) {
+ if (VERSION.SDK_INT >= 34) {
+ return Api34Impl.getMslAltitudeMeters(location);
+ }
Preconditions.checkState(hasMslAltitude(location),
"The Mean Sea Level altitude of the location is not set.");
return getOrCreateExtras(location).getDouble(EXTRA_MSL_ALTITUDE);
@@ -305,24 +314,54 @@
/**
* Sets the Mean Sea Level altitude of the location in meters.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude does not exist. In
+ * order to allow for backwards compatibility and testing however, this method will attempt
+ * to set a double extra with the key {@link #EXTRA_MSL_ALTITUDE} to include Mean Sea Level
+ * altitude. Be aware that this will overwrite any prior extra value under the same key.
+ *
+ * @see Location#setMslAltitudeMeters(double)
*/
public static void setMslAltitudeMeters(@NonNull Location location,
double mslAltitudeMeters) {
- getOrCreateExtras(location).putDouble(EXTRA_MSL_ALTITUDE, mslAltitudeMeters);
+ if (VERSION.SDK_INT >= 34) {
+ Api34Impl.setMslAltitudeMeters(location, mslAltitudeMeters);
+ } else {
+ getOrCreateExtras(location).putDouble(EXTRA_MSL_ALTITUDE, mslAltitudeMeters);
+ }
}
/**
* Returns true if the location has a Mean Sea Level altitude, false otherwise.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude does not exist. In
+ * order to allow for backwards compatibility and testing however, this method will return
+ * true if an extra value is with the key {@link #EXTRA_MSL_ALTITUDE}.
+ *
+ * @see Location#hasMslAltitude()
*/
public static boolean hasMslAltitude(@NonNull Location location) {
+ if (VERSION.SDK_INT >= 34) {
+ return Api34Impl.hasMslAltitude(location);
+ }
return containsExtra(location, EXTRA_MSL_ALTITUDE);
}
/**
* Removes the Mean Sea Level altitude from the location.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude does not exist. In
+ * order to allow for backwards compatibility and testing however, this method will attempt
+ * to remove any extra value with the key {@link #EXTRA_MSL_ALTITUDE}.
+ *
+ * @see Location#removeMslAltitude()
*/
public static void removeMslAltitude(@NonNull Location location) {
- removeExtra(location, EXTRA_MSL_ALTITUDE);
+ if (VERSION.SDK_INT >= 34) {
+ Api34Impl.removeMslAltitude(location);
+ } else {
+ removeExtra(location, EXTRA_MSL_ALTITUDE);
+ }
}
/**
@@ -331,11 +370,20 @@
* altitude of the location falls within {@link #getMslAltitudeMeters(Location)} +/- this
* uncertainty.
*
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude accuracy does not
+ * exist. In order to allow for backwards compatibility and testing however, this method will
+ * attempt to read a float extra with the key {@link #EXTRA_MSL_ALTITUDE_ACCURACY} and return
+ * the result.
+ *
* @throws IllegalStateException if the Mean Sea Level altitude accuracy of the location is not
* set
+ * @see Location#setMslAltitudeAccuracyMeters(float)
*/
public static @FloatRange(from = 0.0) float getMslAltitudeAccuracyMeters(
@NonNull Location location) {
+ if (VERSION.SDK_INT >= 34) {
+ return Api34Impl.getMslAltitudeAccuracyMeters(location);
+ }
Preconditions.checkState(hasMslAltitudeAccuracy(location),
"The Mean Sea Level altitude accuracy of the location is not set.");
return getOrCreateExtras(location).getFloat(EXTRA_MSL_ALTITUDE_ACCURACY);
@@ -343,25 +391,56 @@
/**
* Sets the Mean Sea Level altitude accuracy of the location in meters.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude accuracy does not
+ * exist. In order to allow for backwards compatibility and testing however, this method will
+ * attempt to set a float extra with the key {@link #EXTRA_MSL_ALTITUDE_ACCURACY} to include
+ * Mean Sea Level altitude accuracy. Be aware that this will overwrite any prior extra value
+ * under the same key.
+ *
+ * @see Location#setMslAltitudeAccuracyMeters(float)
*/
public static void setMslAltitudeAccuracyMeters(@NonNull Location location,
@FloatRange(from = 0.0) float mslAltitudeAccuracyMeters) {
- getOrCreateExtras(location).putFloat(EXTRA_MSL_ALTITUDE_ACCURACY,
- mslAltitudeAccuracyMeters);
+ if (VERSION.SDK_INT >= 34) {
+ Api34Impl.setMslAltitudeAccuracyMeters(location, mslAltitudeAccuracyMeters);
+ } else {
+ getOrCreateExtras(location).putFloat(EXTRA_MSL_ALTITUDE_ACCURACY,
+ mslAltitudeAccuracyMeters);
+ }
}
/**
* Returns true if the location has a Mean Sea Level altitude accuracy, false otherwise.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude accuracy does not
+ * exist. In order to allow for backwards compatibility and testing however, this method will
+ * return true if an extra value is with the key {@link #EXTRA_MSL_ALTITUDE_ACCURACY}.
+ *
+ * @see Location#hasMslAltitudeAccuracy()
*/
public static boolean hasMslAltitudeAccuracy(@NonNull Location location) {
+ if (VERSION.SDK_INT >= 34) {
+ return Api34Impl.hasMslAltitudeAccuracy(location);
+ }
return containsExtra(location, EXTRA_MSL_ALTITUDE_ACCURACY);
}
/**
* Removes the Mean Sea Level altitude accuracy from the location.
+ *
+ * <p>NOTE: On API levels below 34, the concept of Mean Sea Level altitude accuracy does not
+ * exist. In order to allow for backwards compatibility and testing however, this method will
+ * attempt to remove any extra value with the key {@link #EXTRA_MSL_ALTITUDE_ACCURACY}.
+ *
+ * @see Location#removeMslAltitudeAccuracy()
*/
public static void removeMslAltitudeAccuracy(@NonNull Location location) {
- removeExtra(location, EXTRA_MSL_ALTITUDE_ACCURACY);
+ if (VERSION.SDK_INT >= 34) {
+ Api34Impl.removeMslAltitudeAccuracy(location);
+ } else {
+ removeExtra(location, EXTRA_MSL_ALTITUDE_ACCURACY);
+ }
}
/**
@@ -433,10 +512,59 @@
}
}
+ @RequiresApi(34)
+ private static class Api34Impl {
+
+ private Api34Impl() {
+ }
+
+ @DoNotInline
+ static double getMslAltitudeMeters(Location location) {
+ return location.getMslAltitudeMeters();
+ }
+
+ @DoNotInline
+ static void setMslAltitudeMeters(Location location, double mslAltitudeMeters) {
+ location.setMslAltitudeMeters(mslAltitudeMeters);
+ }
+
+ @DoNotInline
+ static boolean hasMslAltitude(Location location) {
+ return location.hasMslAltitude();
+ }
+
+ @DoNotInline
+ static void removeMslAltitude(Location location) {
+ location.removeMslAltitude();
+ }
+
+ @DoNotInline
+ static float getMslAltitudeAccuracyMeters(Location location) {
+ return location.getMslAltitudeAccuracyMeters();
+ }
+
+ @DoNotInline
+ static void setMslAltitudeAccuracyMeters(Location location,
+ float mslAltitudeAccuracyMeters) {
+ location.setMslAltitudeAccuracyMeters(mslAltitudeAccuracyMeters);
+ }
+
+ @DoNotInline
+ static boolean hasMslAltitudeAccuracy(Location location) {
+ return location.hasMslAltitudeAccuracy();
+ }
+
+ @DoNotInline
+ static void removeMslAltitudeAccuracy(Location location) {
+ location.removeMslAltitudeAccuracy();
+ }
+ }
+
@RequiresApi(26)
private static class Api26Impl {
- private Api26Impl() {}
+ private Api26Impl() {
+ }
@DoNotInline
static boolean hasVerticalAccuracy(Location location) {
@@ -487,7 +615,8 @@
@RequiresApi(18)
private static class Api18Impl {
- private Api18Impl() {}
+ private Api18Impl() {
+ }
@DoNotInline
static boolean isMock(Location location) {
@@ -498,7 +627,8 @@
@RequiresApi(17)
private static class Api17Impl {
- private Api17Impl() {}
+ private Api17Impl() {
+ }
@DoNotInline
static long getElapsedRealtimeNanos(Location location) {
diff --git a/core/core/src/main/java/androidx/core/service/quicksettings/PendingIntentActivityWrapper.java b/core/core/src/main/java/androidx/core/service/quicksettings/PendingIntentActivityWrapper.java
new file mode 100644
index 0000000..d42dd7c
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/service/quicksettings/PendingIntentActivityWrapper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.service.quicksettings;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.quicksettings.TileService;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.PendingIntentCompat;
+
+/**
+ * A wrapper class for developers to use with
+ * {@link TileServiceCompat#startActivityAndCollapse(TileService, PendingIntentActivityWrapper)}.
+ */
+public class PendingIntentActivityWrapper {
+
+ private final Context mContext;
+
+ private final int mRequestCode;
+
+ @NonNull
+ private final Intent mIntent;
+
+ @PendingIntentCompat.Flags
+ private final int mFlags;
+
+ @Nullable
+ private final Bundle mOptions;
+
+ @Nullable
+ private final PendingIntent mPendingIntent;
+
+ private final boolean mIsMutable;
+
+ public PendingIntentActivityWrapper(@NonNull Context context, int requestCode,
+ @NonNull Intent intent,
+ @PendingIntentCompat.Flags int flags, boolean isMutable) {
+ this(context, requestCode, intent, flags, null, isMutable);
+ }
+
+ public PendingIntentActivityWrapper(@NonNull Context context, int requestCode,
+ @NonNull Intent intent,
+ @PendingIntentCompat.Flags int flags, @Nullable Bundle options, boolean isMutable) {
+ this.mContext = context;
+ this.mRequestCode = requestCode;
+ this.mIntent = intent;
+ this.mFlags = flags;
+ this.mOptions = options;
+ this.mIsMutable = isMutable;
+
+ mPendingIntent = createPendingIntent();
+ }
+
+ public @NonNull Context getContext() {
+ return mContext;
+ }
+
+ public int getRequestCode() {
+ return mRequestCode;
+ }
+
+ public @NonNull Intent getIntent() {
+ return mIntent;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+
+ public @NonNull Bundle getOptions() {
+ return mOptions;
+ }
+
+ public boolean isMutable() {
+ return mIsMutable;
+ }
+
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ private @Nullable PendingIntent createPendingIntent() {
+ if (mOptions == null) {
+ return PendingIntentCompat.getActivity(mContext, mRequestCode, mIntent, mFlags,
+ mIsMutable);
+ }
+ return PendingIntentCompat.getActivity(mContext, mRequestCode, mIntent, mFlags, mOptions,
+ mIsMutable);
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java b/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java
new file mode 100644
index 0000000..bb55828
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.service.quicksettings;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.service.quicksettings.TileService;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+/**
+ * A helper for accessing {@link TileService} API methods.
+ */
+public class TileServiceCompat {
+
+ private static TileServiceWrapper sTileServiceWrapper;
+
+ /**
+ * Calls the correct {@link TileService}#startActivityAndCollapse() method
+ * depending on the app's targeted {@link android.os.Build.VERSION_CODES}.
+ */
+ public static void startActivityAndCollapse(@NonNull TileService tileService,
+ @NonNull PendingIntentActivityWrapper wrapper) {
+ if (SDK_INT >= 34) {
+ if (sTileServiceWrapper != null) {
+ sTileServiceWrapper.startActivityAndCollapse(wrapper.getPendingIntent());
+ } else {
+ Api34Impl.startActivityAndCollapse(tileService, wrapper.getPendingIntent());
+ }
+ } else if (SDK_INT >= 24) {
+ if (sTileServiceWrapper != null) {
+ sTileServiceWrapper.startActivityAndCollapse(wrapper.getIntent());
+ } else {
+ Api24Impl.startActivityAndCollapse(tileService, wrapper.getIntent());
+ }
+ }
+ }
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public static void setTileServiceWrapper(@NonNull TileServiceWrapper serviceWrapper) {
+ sTileServiceWrapper = serviceWrapper;
+ }
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public static void clearTileServiceWrapper() {
+ sTileServiceWrapper = null;
+ }
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ @DoNotInline
+ static void startActivityAndCollapse(TileService service,
+ PendingIntent pendingIntent) {
+ service.startActivityAndCollapse(pendingIntent);
+ }
+ }
+
+ @RequiresApi(24)
+ private static class Api24Impl {
+ @DoNotInline
+ static void startActivityAndCollapse(TileService service, Intent intent) {
+ service.startActivityAndCollapse(intent);
+ }
+ }
+
+ private TileServiceCompat() {
+ }
+
+ interface TileServiceWrapper {
+ void startActivityAndCollapse(PendingIntent pendingIntent);
+
+ void startActivityAndCollapse(Intent intent);
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java b/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java
new file mode 100644
index 0000000..81f847e
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.text.util;
+
+import android.icu.number.LocalizedNumberFormatter;
+import android.icu.number.NumberFormatter;
+import android.icu.text.DateFormat;
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.MeasureUnit;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.OptIn;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StringDef;
+import androidx.core.os.BuildCompat;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Locale.Category;
+
+/**
+ * Provides friendly APIs to get the user's locale preferences. The data can refer to
+ * external/cldr/common/main/en.xml.
+ */
+@RequiresApi(VERSION_CODES.LOLLIPOP)
+public final class LocalePreferences {
+ private static final String TAG = LocalePreferences.class.getSimpleName();
+
+ /** APIs to get the user's preference of the hour cycle. */
+ public static class HourCycle {
+ private static final String U_EXTENSION_TAG = "hc";
+
+ /** 12 Hour System (0-11) */
+ public static final String H11 = "h11";
+ /** 12 Hour System (1-12) */
+ public static final String H12 = "h12";
+ /** 24 Hour System (0-23) */
+ public static final String H23 = "h23";
+ /** 24 Hour System (1-24) */
+ public static final String H24 = "h24";
+ /** Default hour cycle for the locale */
+ public static final String DEFAULT = "";
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ H11,
+ H12,
+ H23,
+ H24,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HourCycleTypes {
+ }
+
+ private HourCycle() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the hour cycle which is from
+ * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and
+ * bases on the {@code Locale#getDefault(Locale.Category)}. It is one of the strings defined in
+ * {@see HourCycle}, e.g. {@code HourCycle#H11}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle() {
+ return getHourCycle(true);
+ }
+
+ /**
+ * Return the hour cycle setting of the inputted {@link Locale}. The returned result is resolved
+ * and based on the input {@code Locale}. It is one of the strings defined in
+ * {@see HourCycle}, e.g. {@code HourCycle#H11}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(@NonNull Locale locale) {
+ return getHourCycle(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the hour cycle which is from
+ * {@link Locale#getDefault(Locale.Category)}, e.g. {@code HourCycle#H11}.
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains hour cycle subtag,
+ * this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag
+ * and the resolved argument is true, this function tries to find the default
+ * hour cycle for the {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain hour cycle subtag
+ * and the resolved argument is false, this function returns empty string
+ * , i.e. {@code HourCycle#DEFAULT}.
+ * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified
+ * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string, i.e.
+ * {@code HourCycle#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(
+ boolean resolved) {
+ Locale defaultLocale = (Build.VERSION.SDK_INT >= VERSION_CODES.N)
+ ? Api24Impl.getDefaultLocale()
+ : getDefaultLocale();
+ return getHourCycle(defaultLocale, resolved);
+ }
+
+ /**
+ * Return the hour cycle setting of the inputted {@link Locale}. E.g. "en-US-u-hc-h23".
+ *
+ * @param locale The {@code Locale} to get the hour cycle.
+ * @param resolved If the given {@code Locale} contains hour cycle subtag, this argument is
+ * ignored. If the given {@code Locale} doesn't contain hour cycle subtag and
+ * the resolved argument is true, this function tries to find the default
+ * hour cycle for the given {@code Locale}. If the given {@code Locale} doesn't
+ * contain hour cycle subtag and the resolved argument is false, this function
+ * return empty string, i.e. {@code HourCycle#DEFAULT}.
+ * @return {@link HourCycle.HourCycleTypes} If the malformed hour cycle format was specified
+ * in the hour cycle subtag, e.g. en-US-u-hc-h32, this function returns empty string, i.e.
+ * {@code HourCycle#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @HourCycle.HourCycleTypes
+ public static String getHourCycle(@NonNull Locale locale, boolean resolved) {
+ String result = getUnicodeLocaleType(HourCycle.U_EXTENSION_TAG,
+ HourCycle.DEFAULT, locale, resolved);
+ if (result != null) {
+ return result;
+ }
+ if (BuildCompat.isAtLeastT()) {
+ return Api33Impl.getHourCycle(locale);
+ } else {
+ return getBaseHourCycle(locale);
+ }
+ }
+
+ /** APIs to get the user's preference of Calendar. */
+ public static class CalendarType {
+ private static final String U_EXTENSION_TAG = "ca";
+ /** Chinese Calendar */
+ public static final String CHINESE = "chinese";
+ /** Dangi Calendar (Korea Calendar) */
+ public static final String DANGI = "dangi";
+ /** Gregorian Calendar */
+ public static final String GREGORIAN = "gregorian";
+ /** Hebrew Calendar */
+ public static final String HEBREW = "hebrew";
+ /** Indian National Calendar */
+ public static final String INDIAN = "indian";
+ /** Islamic Calendar */
+ public static final String ISLAMIC = "islamic";
+ /** Islamic Calendar (tabular, civil epoch) */
+ public static final String ISLAMIC_CIVIL = "islamic-civil";
+ /** Islamic Calendar (Saudi Arabia, sighting) */
+ public static final String ISLAMIC_RGSA = "islamic-rgsa";
+ /** Islamic Calendar (tabular, astronomical epoch) */
+ public static final String ISLAMIC_TBLA = "islamic-tbla";
+ /** Islamic Calendar (Umm al-Qura) */
+ public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+ /** Persian Calendar */
+ public static final String PERSIAN = "persian";
+ /** Default calendar for the locale */
+ public static final String DEFAULT = "";
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ CHINESE,
+ DANGI,
+ GREGORIAN,
+ HEBREW,
+ INDIAN,
+ ISLAMIC,
+ ISLAMIC_CIVIL,
+ ISLAMIC_RGSA,
+ ISLAMIC_TBLA,
+ ISLAMIC_UMALQURA,
+ PERSIAN,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CalendarTypes {
+ }
+
+ private CalendarType() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the calendar type which is from {@link
+ * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on
+ * the {@code Locale#getDefault(Locale.Category)} settings. It is one of the strings defined in
+ * {@see CalendarType}, e.g. {@code CalendarType#CHINESE}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType() {
+ return getCalendarType(true);
+ }
+
+ /**
+ * Return the calendar type of the inputted {@link Locale}. The returned result is resolved and
+ * based on the input {@link Locale} settings. It is one of the strings defined in
+ * {@see CalendarType}, e.g. {@code CalendarType#CHINESE}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(@NonNull Locale locale) {
+ return getCalendarType(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the calendar type which is from {@link
+ * Locale#getDefault(Category)}, e.g. {@code CalendarType#CHINESE}.
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains calendar type
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type
+ * subtag and the resolved argument is true, this function tries to find
+ * the default calendar type for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain calendar type
+ * subtag and the resolved argument is false, this function returns empty string
+ * , i.e. {@code CalendarType#DEFAULT}.
+ * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was
+ * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns
+ * empty string, i.e. {@code CalendarType#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(boolean resolved) {
+ Locale defaultLocale = (Build.VERSION.SDK_INT >= VERSION_CODES.N)
+ ? Api24Impl.getDefaultLocale()
+ : getDefaultLocale();
+ return getCalendarType(defaultLocale, resolved);
+ }
+
+ /**
+ * Return the calendar type of the inputted {@link Locale}, e.g. {@code CalendarType#CHINESE}.
+ *
+ * @param locale The {@link Locale} to get the calendar type.
+ * @param resolved If the given {@code Locale} contains calendar type subtag, this argument is
+ * ignored. If the given {@code Locale} doesn't contain calendar type subtag and
+ * the resolved argument is true, this function tries to find the default
+ * calendar type for the given {@code Locale}. If the given {@code Locale}
+ * doesn't contain calendar type subtag and the resolved argument is false, this
+ * function return empty string, i.e. {@code CalendarType#DEFAULT}.
+ * @return {@link CalendarType.CalendarTypes} If the malformed calendar type format was
+ * specified in the calendar type subtag, e.g. en-US-u-ca-calendar, this function returns
+ * empty string, i.e. {@code CalendarType#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @CalendarType.CalendarTypes
+ public static String getCalendarType(@NonNull Locale locale, boolean resolved) {
+ String result = getUnicodeLocaleType(CalendarType.U_EXTENSION_TAG,
+ CalendarType.DEFAULT, locale, resolved);
+ if (result != null) {
+ return result;
+ }
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
+ return Api24Impl.getCalendarType(locale);
+ } else {
+ return resolved ? CalendarType.GREGORIAN : CalendarType.DEFAULT;
+ }
+ }
+
+ /** APIs to get the user's preference of temperature unit. */
+ public static class TemperatureUnit {
+ private static final String U_EXTENSION_TAG = "mu";
+ /** Celsius */
+ public static final String CELSIUS = "celsius";
+ /** Fahrenheit */
+ public static final String FAHRENHEIT = "fahrenhe";
+ /** Kelvin */
+ public static final String KELVIN = "kelvin";
+ /** Default Temperature for the locale */
+ public static final String DEFAULT = "";
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ CELSIUS,
+ FAHRENHEIT,
+ KELVIN,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TemperatureUnits {
+ }
+
+ private TemperatureUnit() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the temperature unit which is from {@link
+ * Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the
+ * {@code Locale#getDefault(Locale.Category)} settings. It is one of the strings defined in
+ * {@see TemperatureUnit}, e.g. {@code TemperatureUnit#FAHRENHEIT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit() {
+ return getTemperatureUnit(true);
+ }
+
+ /**
+ * Return the temperature unit of the inputted {@link Locale}. It is one of the strings
+ * defined in {@see TemperatureUnit}, e.g. {@code TemperatureUnit#FAHRENHEIT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(
+ @NonNull Locale locale) {
+ return getTemperatureUnit(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the temperature unit which is from {@link
+ * Locale#getDefault(Locale.Category)}, e.g. {@code TemperatureUnit#FAHRENHEIT}.
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains temperature unit
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit
+ * subtag and the resolved argument is true, this function tries to find
+ * the default temperature unit for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain temperature unit
+ * subtag and the resolved argument is false, this function returns empty string
+ * , i.e. {@code TemperatureUnit#DEFAULT}.
+ * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was
+ * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns
+ * empty string, i.e. {@code TemperatureUnit#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(boolean resolved) {
+ Locale defaultLocale = (Build.VERSION.SDK_INT >= VERSION_CODES.N)
+ ? Api24Impl.getDefaultLocale()
+ : getDefaultLocale();
+ return getTemperatureUnit(defaultLocale, resolved);
+ }
+
+ /**
+ * Return the temperature unit of the inputted {@link Locale}. E.g. "fahrenheit"
+ *
+ * @param locale The {@link Locale} to get the temperature unit.
+ * @param resolved If the given {@code Locale} contains temperature unit subtag, this argument
+ * is ignored. If the given {@code Locale} doesn't contain temperature unit
+ * subtag and the resolved argument is true, this function tries to find
+ * the default temperature unit for the given {@code Locale}. If the given
+ * {@code Locale} doesn't contain temperature unit subtag and the resolved
+ * argument is false, this function return empty string, i.e.
+ * {@code TemperatureUnit#DEFAULT}.
+ * @return {@link TemperatureUnit.TemperatureUnits} If the malformed temperature unit format was
+ * specified in the temperature unit subtag, e.g. en-US-u-mu-temperature, this function returns
+ * empty string, i.e. {@code TemperatureUnit#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @TemperatureUnit.TemperatureUnits
+ public static String getTemperatureUnit(@NonNull Locale locale, boolean resolved) {
+ String result = getUnicodeLocaleType(TemperatureUnit.U_EXTENSION_TAG,
+ TemperatureUnit.DEFAULT, locale, resolved);
+ if (result != null) {
+ return result;
+ }
+ if (BuildCompat.isAtLeastT()) {
+ return Api33Impl.getResolvedTemperatureUnit(locale);
+ } else {
+ return getTemperatureHardCoded(locale);
+ }
+ }
+
+ /** APIs to get the user's preference of the first day of week. */
+ public static class FirstDayOfWeek {
+ private static final String U_EXTENSION_TAG = "fw";
+ /** Sunday */
+ public static final String SUNDAY = "sun";
+ /** Monday */
+ public static final String MONDAY = "mon";
+ /** Tuesday */
+ public static final String TUESDAY = "tue";
+ /** Wednesday */
+ public static final String WEDNESDAY = "wed";
+ /** Thursday */
+ public static final String THURSDAY = "thu";
+ /** Friday */
+ public static final String FRIDAY = "fri";
+ /** Saturday */
+ public static final String SATURDAY = "sat";
+ /** Default first day of week for the locale */
+ public static final String DEFAULT = "";
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @StringDef({
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY,
+ DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Days {
+ }
+
+ private FirstDayOfWeek() {
+ }
+ }
+
+ /**
+ * Return the user's preference of the first day of week which is from
+ * {@link Locale#getDefault(Locale.Category)}. The returned result is resolved and bases on the
+ * {@code Locale#getDefault(Locale.Category)} settings. It is one of the strings defined in
+ * {@see FirstDayOfWeek}, e.g. {@code FirstDayOfWeek#SUNDAY}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek() {
+ return getFirstDayOfWeek(true);
+ }
+
+ /**
+ * Return the first day of week of the inputted {@link Locale}. The returned result is resolved
+ * and based on the input {@code Locale} settings. It is one of the strings defined in
+ * {@see FirstDayOfWeek}, e.g. {@code FirstDayOfWeek#SUNDAY}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek(@NonNull Locale locale) {
+ return getFirstDayOfWeek(locale, true);
+ }
+
+ /**
+ * Return the user's preference of the first day of week which is from {@link
+ * Locale#getDefault(Locale.Category)}, e.g. {@code FirstDayOfWeek#SUNDAY}.
+ *
+ * @param resolved If the {@code Locale#getDefault(Locale.Category)} contains first day of week
+ * subtag, this argument is ignored. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week
+ * subtag and the resolved argument is true, this function tries to find
+ * the default first day of week for the
+ * {@code Locale#getDefault(Locale.Category)}. If the
+ * {@code Locale#getDefault(Locale.Category)} doesn't contain first day of week
+ * subtag and the resolved argument is false, this function returns empty string
+ * , i.e. {@code FirstDayOfWeek#DEFAULT}.
+ * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was specified
+ * in the first day of week subtag, e.g. en-US-u-fw-days, this function returns empty string,
+ * i.e. {@code FirstDayOfWeek#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek(boolean resolved) {
+ Locale defaultLocale = (Build.VERSION.SDK_INT >= VERSION_CODES.N)
+ ? Api24Impl.getDefaultLocale()
+ : getDefaultLocale();
+ return getFirstDayOfWeek(defaultLocale, resolved);
+ }
+
+ /**
+ * Return the first day of week of the inputted {@link Locale},
+ * e.g. {@code FirstDayOfWeek#SUNDAY}.
+ *
+ * @param locale The {@link Locale} to get the first day of week.
+ * @param resolved If the given {@code Locale} contains first day of week subtag, this argument
+ * is ignored. If the given {@code Locale} doesn't contain first day of week
+ * subtag and the resolved argument is true, this function tries to find
+ * the default first day of week for the given {@code Locale}. If the given
+ * {@code Locale} doesn't contain first day of week subtag and the resolved
+ * argument is false, this function return empty string, i.e.
+ * {@code FirstDayOfWeek#DEFAULT}.
+ * @return {@link FirstDayOfWeek.Days} If the malformed first day of week format was
+ * specified in the first day of week subtag, e.g. en-US-u-fw-days, this function returns
+ * empty string, i.e. {@code FirstDayOfWeek#DEFAULT}.
+ */
+ @NonNull
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @FirstDayOfWeek.Days
+ public static String getFirstDayOfWeek(
+ @NonNull Locale locale, boolean resolved) {
+ String result = getUnicodeLocaleType(FirstDayOfWeek.U_EXTENSION_TAG,
+ FirstDayOfWeek.DEFAULT, locale, resolved);
+ return result != null ? result : getBaseFirstDayOfWeek(locale);
+ }
+
+ private static String getUnicodeLocaleType(String tag, String defaultValue, Locale locale,
+ boolean resolved) {
+ String ext = locale.getUnicodeLocaleType(tag);
+ if (ext != null) {
+ return ext;
+ }
+ if (!resolved) {
+ return defaultValue;
+ }
+ return null;
+ }
+
+
+ // Warning: This list of country IDs must be in alphabetical order for binarySearch to
+ // work correctly.
+ private static final String[] WEATHER_FAHRENHEIT_COUNTRIES =
+ {"BS", "BZ", "KY", "PR", "PW", "US"};
+
+ @TemperatureUnit.TemperatureUnits
+ private static String getTemperatureHardCoded(Locale locale) {
+ return Arrays.binarySearch(WEATHER_FAHRENHEIT_COUNTRIES, locale.getCountry()) >= 0
+ ? TemperatureUnit.FAHRENHEIT
+ : TemperatureUnit.CELSIUS;
+ }
+
+ @HourCycle.HourCycleTypes
+ private static String getBaseHourCycle(@NonNull Locale locale) {
+ String pattern =
+ android.text.format.DateFormat.getBestDateTimePattern(
+ locale, "jm");
+ return pattern.contains("H") ? HourCycle.H23 : HourCycle.H12;
+ }
+
+ @FirstDayOfWeek.Days
+ private static String getBaseFirstDayOfWeek(@NonNull Locale locale) {
+ // A known bug affects both the {@code android.icu.util.Calendar} and
+ // {@code java.util.Calendar}: they ignore the "fw" field in the -u- extension, even if
+ // present. So please do not remove the explicit check on getUnicodeLocaleType,
+ // which protects us from that bug.
+ return getStringOfFirstDayOfWeek(
+ java.util.Calendar.getInstance(locale).getFirstDayOfWeek());
+ }
+
+ private static String getStringOfFirstDayOfWeek(int fw) {
+ String[] arrDays = {
+ FirstDayOfWeek.SUNDAY,
+ FirstDayOfWeek.MONDAY,
+ FirstDayOfWeek.TUESDAY,
+ FirstDayOfWeek.WEDNESDAY,
+ FirstDayOfWeek.THURSDAY,
+ FirstDayOfWeek.FRIDAY,
+ FirstDayOfWeek.SATURDAY};
+ return fw >= 1 && fw <= 7 ? arrDays[fw - 1] : FirstDayOfWeek.DEFAULT;
+ }
+
+ private static Locale getDefaultLocale() {
+ return Locale.getDefault();
+ }
+
+ @RequiresApi(VERSION_CODES.N)
+ private static class Api24Impl {
+ @DoNotInline
+ @CalendarType.CalendarTypes
+ static String getCalendarType(@NonNull Locale locale) {
+ return android.icu.util.Calendar.getInstance(locale).getType();
+ }
+
+ @DoNotInline
+ static Locale getDefaultLocale() {
+ return Locale.getDefault(Category.FORMAT);
+ }
+
+ private Api24Impl() {
+ }
+ }
+
+ @RequiresApi(VERSION_CODES.TIRAMISU)
+ private static class Api33Impl {
+ @DoNotInline
+ @TemperatureUnit.TemperatureUnits
+ static String getResolvedTemperatureUnit(@NonNull Locale locale) {
+ LocalizedNumberFormatter nf = NumberFormatter.with()
+ .usage("weather")
+ .unit(MeasureUnit.CELSIUS)
+ .locale(locale);
+ String unit = nf.format(1).getOutputUnit().getIdentifier();
+ if (unit.startsWith(TemperatureUnit.FAHRENHEIT)) {
+ return TemperatureUnit.FAHRENHEIT;
+ }
+ return unit;
+ }
+
+ @DoNotInline
+ @HourCycle.HourCycleTypes
+ static String getHourCycle(@NonNull Locale locale) {
+ return getHourCycleType(
+ DateTimePatternGenerator.getInstance(locale).getDefaultHourCycle());
+ }
+
+ @HourCycle.HourCycleTypes
+ private static String getHourCycleType(
+ DateFormat.HourCycle hourCycle) {
+ switch (hourCycle) {
+ case HOUR_CYCLE_11:
+ return HourCycle.H11;
+ case HOUR_CYCLE_12:
+ return HourCycle.H12;
+ case HOUR_CYCLE_23:
+ return HourCycle.H23;
+ case HOUR_CYCLE_24:
+ return HourCycle.H24;
+ default:
+ return HourCycle.DEFAULT;
+ }
+ }
+
+ private Api33Impl() {
+ }
+ }
+
+ private LocalePreferences() {
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/util/Function.java b/core/core/src/main/java/androidx/core/util/Function.java
new file mode 100644
index 0000000..682c961
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/util/Function.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.core.util;
+
+/**
+ * Compat version of {@link java.util.function.Function}
+ * @param <T> the type of the input to the operation
+ * @param <R>: the type of the output of the function
+ */
+@FunctionalInterface
+public interface Function<T, R> {
+ /**
+ * Applies the function to the argument parameter.
+ *
+ * @param t the argument for the function
+ * @return the result after applying function
+ */
+ R apply(T t);
+}
diff --git a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
new file mode 100644
index 0000000..49f3bab
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.util;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import static android.util.TypedValue.COMPLEX_UNIT_IN;
+import static android.util.TypedValue.COMPLEX_UNIT_MM;
+import static android.util.TypedValue.COMPLEX_UNIT_PT;
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * Container for a dynamically typed data value. Primarily used with
+ * {@link android.content.res.Resources} for holding resource values.
+ *
+ * <p>Used to convert between dimension values like DP and SP to pixels, and vice versa.
+ */
+public class TypedValueCompat {
+ private static final float INCHES_PER_PT = (1.0f / 72);
+ private static final float INCHES_PER_MM = (1.0f / 25.4f);
+
+ private TypedValueCompat() {}
+
+ /**
+ * Converts a pixel value to the given dimension, e.g. PX to DP.
+ *
+ * <p>This is the inverse of {@link TypedValue#applyDimension(int, float, DisplayMetrics)}
+ *
+ * @param unitToConvertTo The unit to convert to.
+ * @param pixelValue The raw pixels value to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A dimension value equivalent to the given number of pixels
+ * @throws IllegalArgumentException if unitToConvertTo is not valid.
+ */
+ public static float deriveDimension(
+ int unitToConvertTo,
+ float pixelValue,
+ @NonNull DisplayMetrics metrics) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.deriveDimension(unitToConvertTo, pixelValue, metrics);
+ }
+
+ switch (unitToConvertTo) {
+ case COMPLEX_UNIT_PX:
+ return pixelValue;
+ case COMPLEX_UNIT_DIP: {
+ // Avoid divide-by-zero, and return 0 since that's what the inverse function will do
+ if (metrics.density == 0) {
+ return 0;
+ }
+ return pixelValue / metrics.density;
+ }
+ case COMPLEX_UNIT_SP:
+ // Versions earlier than U don't get the fancy non-linear scaling
+ if (metrics.scaledDensity == 0) {
+ return 0;
+ }
+ return pixelValue / metrics.scaledDensity;
+ case COMPLEX_UNIT_PT: {
+ if (metrics.xdpi == 0) {
+ return 0;
+ }
+ return pixelValue / metrics.xdpi / INCHES_PER_PT;
+ }
+ case COMPLEX_UNIT_IN: {
+ if (metrics.xdpi == 0) {
+ return 0;
+ }
+ return pixelValue / metrics.xdpi;
+ }
+ case COMPLEX_UNIT_MM: {
+ if (metrics.xdpi == 0) {
+ return 0;
+ }
+ return pixelValue / metrics.xdpi / INCHES_PER_MM;
+ }
+ default:
+ throw new IllegalArgumentException("Invalid unitToConvertTo " + unitToConvertTo);
+ }
+ }
+
+ /**
+ * Converts a density-independent pixels (DP) value to pixels
+ *
+ * <p>This is a convenience function for
+ * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}
+ *
+ * @param dpValue The value in DP to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A raw pixel value
+ */
+ public static float dpToPx(float dpValue, @NonNull DisplayMetrics metrics) {
+ return TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, metrics);
+ }
+
+ /**
+ * Converts a pixel value to density-independent pixels (DP)
+ *
+ * <p>This is a convenience function for {@link #deriveDimension(int, float, DisplayMetrics)}
+ *
+ * @param pixelValue The raw pixels value to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A dimension value (in DP) representing the given number of pixels.
+ */
+ public static float pxToDp(float pixelValue, @NonNull DisplayMetrics metrics) {
+ return deriveDimension(COMPLEX_UNIT_DIP, pixelValue, metrics);
+ }
+
+ /**
+ * Converts a scaled pixels (SP) value to pixels
+ *
+ * <p>This is a convenience function for
+ * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}
+ *
+ * @param spValue The value in SP to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A raw pixel value
+ */
+ public static float spToPx(float spValue, @NonNull DisplayMetrics metrics) {
+ return TypedValue.applyDimension(COMPLEX_UNIT_SP, spValue, metrics);
+ }
+
+ /**
+ * Converts a pixel value to scaled pixels (SP)
+ *
+ * <p>This is a convenience function for {@link #deriveDimension(int, float, DisplayMetrics)}
+ *
+ * @param pixelValue The raw pixels value to convert from.
+ * @param metrics Current display metrics to use in the conversion --
+ * supplies display density and scaling information.
+ *
+ * @return A dimension value (in SP) representing the given number of pixels.
+ */
+ public static float pxToSp(float pixelValue, @NonNull DisplayMetrics metrics) {
+ return deriveDimension(COMPLEX_UNIT_SP, pixelValue, metrics);
+ }
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ @DoNotInline
+ public static float deriveDimension(int unitToConvertTo, float pixelValue,
+ DisplayMetrics metrics) {
+ return TypedValue.deriveDimension(unitToConvertTo, pixelValue, metrics);
+ }
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java b/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
index a0d31d1..23fb371 100644
--- a/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
@@ -16,18 +16,35 @@
package androidx.core.view;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.os.Build;
+import android.view.MotionEvent;
import android.view.VelocityTracker;
-/**
- * Helper for accessing features in {@link VelocityTracker}.
- *
- * @deprecated Use {@link VelocityTracker} directly.
- */
-@Deprecated
+import androidx.annotation.DoNotInline;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+
+/** Helper for accessing features in {@link VelocityTracker}. */
public final class VelocityTrackerCompat {
+ @RestrictTo(LIBRARY_GROUP_PREFIX)
+ @Retention(SOURCE)
+ @IntDef(value = {
+ MotionEvent.AXIS_X,
+ MotionEvent.AXIS_Y,
+ MotionEvent.AXIS_SCROLL
+ })
+ public @interface VelocityTrackableMotionEventAxis {}
/**
* Call {@link VelocityTracker#getXVelocity(int)}.
- * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
+ * If running on a pre-{@link Build.VERSION_CODES#HONEYCOMB} device,
* returns {@link VelocityTracker#getXVelocity()}.
*
* @deprecated Use {@link VelocityTracker#getXVelocity(int)} directly.
@@ -39,7 +56,7 @@
/**
* Call {@link VelocityTracker#getYVelocity(int)}.
- * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} device,
+ * If running on a pre-{@link Build.VERSION_CODES#HONEYCOMB} device,
* returns {@link VelocityTracker#getYVelocity()}.
*
* @deprecated Use {@link VelocityTracker#getYVelocity(int)} directly.
@@ -49,5 +66,119 @@
return tracker.getYVelocity(pointerId);
}
+ /**
+ * Checks whether a given velocity-trackable {@link MotionEvent} axis is supported for velocity
+ * tracking by this {@link VelocityTracker} instance (refer to
+ * {@link #getAxisVelocity(VelocityTracker, int, int)} for a list of potentially
+ * velocity-trackable axes).
+ *
+ * <p>Note that the value returned from this method will stay the same for a given instance, so
+ * a single check for axis support is enough per a {@link VelocityTracker} instance.
+ *
+ * @param tracker The {@link VelocityTracker} for which to check axis support.
+ * @param axis The axis to check for velocity support.
+ * @return {@code true} if {@code axis} is supported for velocity tracking, or {@code false}
+ * otherwise.
+ * @see #getAxisVelocity(VelocityTracker, int, int)
+ * @see #getAxisVelocity(VelocityTracker, int)
+ */
+ public static boolean isAxisSupported(@NonNull VelocityTracker tracker,
+ @VelocityTrackableMotionEventAxis int axis) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.isAxisSupported(tracker, axis);
+ }
+ return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y;
+ }
+
+ /**
+ * Equivalent to calling {@link #getAxisVelocity(VelocityTracker, int, int)} for {@code axis}
+ * and the active pointer.
+ *
+ * @param tracker The {@link VelocityTracker} from which to get axis velocity.
+ * @param axis Which axis' velocity to return.
+ * @return The previously computed velocity for {@code axis} for the active pointer if
+ * {@code axis} is supported for velocity tracking, or 0 if velocity tracking is not
+ * supported for the axis.
+ * @see #isAxisSupported(VelocityTracker, int)
+ * @see #getAxisVelocity(VelocityTracker, int, int)
+ */
+ public static float getAxisVelocity(@NonNull VelocityTracker tracker,
+ @VelocityTrackableMotionEventAxis int axis) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.getAxisVelocity(tracker, axis);
+ }
+ if (axis == MotionEvent.AXIS_X) {
+ return tracker.getXVelocity();
+ }
+ if (axis == MotionEvent.AXIS_Y) {
+ return tracker.getYVelocity();
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the last computed velocity for a given motion axis. You must first call
+ * {@link VelocityTracker#computeCurrentVelocity(int)} or
+ * {@link VelocityTracker#computeCurrentVelocity(int, float)} before calling this function.
+ *
+ * <p>In addition to {@link MotionEvent#AXIS_X} and {@link MotionEvent#AXIS_Y} which have been
+ * supported since the introduction of this class, the following axes can be candidates for this
+ * method:
+ * <ul>
+ * <li> {@link MotionEvent#AXIS_SCROLL}: supported starting
+ * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}
+ * </ul>
+ *
+ * <p>Before accessing velocities of an axis using this method, check that your
+ * {@link VelocityTracker} instance supports the axis by using
+ * {@link #isAxisSupported(VelocityTracker, int)}.
+ *
+ * @param tracker The {@link VelocityTracker} from which to get axis velocity.
+ * @param axis Which axis' velocity to return.
+ * @param pointerId Which pointer's velocity to return.
+ * @return The previously computed velocity for {@code axis} for pointer ID of {@code id} if
+ * {@code axis} is supported for velocity tracking, or 0 if velocity tracking is not
+ * supported for the axis.
+ * @see #isAxisSupported(VelocityTracker, int)
+ */
+ public static float getAxisVelocity(
+ @NonNull VelocityTracker tracker,
+ @VelocityTrackableMotionEventAxis int axis,
+ int pointerId) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.getAxisVelocity(tracker, axis, pointerId);
+ }
+ if (axis == MotionEvent.AXIS_X) {
+ return tracker.getXVelocity(pointerId);
+ }
+ if (axis == MotionEvent.AXIS_Y) {
+ return tracker.getYVelocity(pointerId);
+ }
+ return 0;
+
+ }
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static boolean isAxisSupported(VelocityTracker velocityTracker, int axis) {
+ return velocityTracker.isAxisSupported(axis);
+ }
+
+ @DoNotInline
+ static float getAxisVelocity(VelocityTracker velocityTracker, int axis, int id) {
+ return velocityTracker.getAxisVelocity(axis, id);
+ }
+
+ @DoNotInline
+ static float getAxisVelocity(VelocityTracker velocityTracker, int axis) {
+ return velocityTracker.getAxisVelocity(axis);
+ }
+ }
+
private VelocityTrackerCompat() {}
}
diff --git a/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java b/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
index 628a379..597e8928 100644
--- a/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
@@ -18,9 +18,13 @@
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.input.InputManager;
import android.os.Build;
import android.util.Log;
import android.util.TypedValue;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import androidx.annotation.DoNotInline;
@@ -147,11 +151,133 @@
return Api28Impl.shouldShowMenuShortcutsWhenKeyboardPresent(config);
}
final Resources res = context.getResources();
- final int platformResId = res.getIdentifier(
- "config_showMenuShortcutsWhenKeyboardPresent", "bool", "android");
+ final int platformResId =
+ getPlatformResId(res, "config_showMenuShortcutsWhenKeyboardPresent", "bool");
return platformResId != 0 && res.getBoolean(platformResId);
}
+ /**
+ * Minimum absolute value of velocity to initiate a fling for a motion generated by an
+ * {@link InputDevice} with an id of {@code inputDeviceId}, from an input {@code source} and on
+ * a given motion event {@code axis}.
+ *
+ * <p>Before utilizing this method to get a minimum fling velocity for a motion generated by the
+ * input device, scale the velocity of the motion events generated by the input device to pixels
+ * per second.
+ *
+ * <p>For instance, if you tracked {@link MotionEvent#AXIS_SCROLL} vertical velocities generated
+ * from a {@link InputDevice#SOURCE_ROTARY_ENCODER}, the velocity returned from
+ * {@link VelocityTracker} will be in the units with which the axis values were reported in the
+ * motion event. Before comparing that velocity against the minimum fling velocity specified
+ * here, make sure that the {@link MotionEvent#AXIS_SCROLL} velocity from the tracker is
+ * calculated in "units per second" (see {@link VelocityTracker#computeCurrentVelocity(int)},
+ * {@link VelocityTracker#computeCurrentVelocity(int, float)} to adjust your velocity
+ * computations to "per second"), and use {@link #getScaledVerticalScrollFactor} to change this
+ * velocity value to "pixels/second".
+ *
+ * <p>If the provided {@code inputDeviceId} is not valid, or if the input device whose ID is
+ * provided does not support the given motion event source and/or axis, this method will return
+ * {@code Integer.MAX_VALUE}.
+ *
+ * <h3>Obtaining the correct arguments for this method call</h3>
+ * <p><b>inputDeviceId</b>: if calling this method in response to a {@link MotionEvent}, use
+ * the device ID that is reported by the event, which can be obtained using
+ * {@link MotionEvent#getDeviceId()}. Otherwise, use a valid ID that is obtained from
+ * {@link InputDevice#getId()}, or from an {@link InputManager} instance
+ * ({@link InputManager#getInputDeviceIds()} gives all the valid input device IDs).
+ *
+ * <p><b>axis</b>: a {@link MotionEvent} may report data for multiple axes, and each axis may
+ * have multiple data points for different pointers. Use the axis for which you obtained the
+ * velocity for ({@link VelocityTracker} lets you calculate velocities for a specific axis. Use
+ * the axis for which you calculated velocity). You can use
+ * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the
+ * {@link InputDevice}, from which you can derive all the valid axes for the device.
+ *
+ * <p><b>source</b>: use {@link MotionEvent#getSource()} if calling this method in response to a
+ * {@link MotionEvent}. Otherwise, use a valid source for the {@link InputDevice}. You can use
+ * {@link InputDevice#getMotionRanges()} to get all the {@link InputDevice.MotionRange}s for the
+ * {@link InputDevice}, from which you can derive all the valid sources for the device.
+ *
+ *
+ * <p>This method optimizes calls over multiple input device IDs, so caching the return value of
+ * the method is not necessary if you are handling multiple input devices.
+ *
+ * @param context the {@link Context} associated with the view.
+ * @param config the {@link ViewConfiguration} to derive the minimum fling velocity from.
+ * @param inputDeviceId the ID of the {@link InputDevice} that generated the motion triggering
+ * fling.
+ * @param axis the axis on which the motion triggering the fling happened. This axis should be
+ * a valid axis that can be reported by the provided input device from the provided
+ * input device source.
+ * @param source the input source of the motion causing fling. This source should be a valid
+ * source for the {@link InputDevice} whose ID is {@code inputDeviceId}.
+ *
+ * @return the minimum velocity, in pixels/second, to trigger fling.
+ *
+ * @see InputDevice#getMotionRange(int, int)
+ * @see InputDevice#getMotionRanges()
+ * @see VelocityTracker#getAxisVelocity(int, int)
+ * @see VelocityTracker#getAxisVelocity(int)
+ */
+ public static int getScaledMinimumFlingVelocity(
+ @NonNull Context context,
+ @NonNull ViewConfiguration config,
+ int inputDeviceId,
+ int axis,
+ int source) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.getScaledMinimumFlingVelocity(config, inputDeviceId, axis, source);
+ }
+
+ if (!isInputDeviceInfoValid(inputDeviceId, axis, source)) {
+ return Integer.MAX_VALUE;
+ }
+
+ Resources res = context.getResources();
+ int platformResId = getPreApi34MinimumFlingVelocityResId(res, source, axis);
+ if (platformResId != 0) {
+ int minFlingVelocity = res.getDimensionPixelSize(platformResId);
+ return minFlingVelocity < 0 ? Integer.MAX_VALUE : minFlingVelocity;
+ }
+
+ return config.getScaledMinimumFlingVelocity();
+ }
+
+ /**
+ * Maximum absolute value of velocity to initiate a fling for a motion generated by an
+ * {@link InputDevice} with an id of {@code inputDeviceId}, from an input {@code source} and on
+ * a given motion event {@code axis}.
+ *
+ * <p>Similar to
+ * {@link #getScaledMinimumFlingVelocity(Context, ViewConfiguration, int, int, int)}, but for
+ * maximum fling velocity, instead of minimum. Also, unlike that method which returns
+ * {@code Integer.MAX_VALUE} for bad input device ID, source and/or motion event axis inputs,
+ * this method returns {@code Integer.MIN_VALUE} for such bad inputs.
+ */
+ public static int getScaledMaximumFlingVelocity(
+ @NonNull Context context,
+ @NonNull ViewConfiguration config,
+ int inputDeviceId,
+ int axis,
+ int source) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.getScaledMaximumFlingVelocity(config, inputDeviceId, axis, source);
+ }
+
+ if (!isInputDeviceInfoValid(inputDeviceId, axis, source)) {
+ return Integer.MIN_VALUE;
+ }
+
+ Resources res = context.getResources();
+ int platformResId = getPreApi34MaximumFlingVelocityResId(res, source, axis);
+ if (platformResId != 0) {
+ int maxFlingVelocity = res.getDimensionPixelSize(platformResId);
+ return maxFlingVelocity < 0 ? Integer.MIN_VALUE : maxFlingVelocity;
+ }
+
+ return config.getScaledMaximumFlingVelocity();
+ }
+
private ViewConfigurationCompat() {
}
@@ -189,4 +315,52 @@
return viewConfiguration.shouldShowMenuShortcutsWhenKeyboardPresent();
}
}
+
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static int getScaledMaximumFlingVelocity(
+ @NonNull ViewConfiguration viewConfiguration,
+ int inputDeviceId,
+ int axis,
+ int source) {
+ return viewConfiguration.getScaledMaximumFlingVelocity(inputDeviceId, axis, source);
+ }
+
+ @DoNotInline
+ static int getScaledMinimumFlingVelocity(
+ @NonNull ViewConfiguration viewConfiguration,
+ int inputDeviceId,
+ int axis,
+ int source) {
+ return viewConfiguration.getScaledMinimumFlingVelocity(inputDeviceId, axis, source);
+ }
+ }
+
+ private static int getPreApi34MaximumFlingVelocityResId(Resources res, int source, int axis) {
+ if (source == InputDeviceCompat.SOURCE_ROTARY_ENCODER && axis == MotionEvent.AXIS_SCROLL) {
+ return getPlatformResId(res, "config_viewMaxRotaryEncoderFlingVelocity", "dimen");
+ }
+ return 0;
+ }
+
+ private static int getPreApi34MinimumFlingVelocityResId(Resources res, int source, int axis) {
+ if (source == InputDeviceCompat.SOURCE_ROTARY_ENCODER && axis == MotionEvent.AXIS_SCROLL) {
+ return getPlatformResId(res, "config_viewMinRotaryEncoderFlingVelocity", "dimen");
+ }
+ return 0;
+ }
+
+ private static int getPlatformResId(Resources res, String name, String defType) {
+ return res.getIdentifier(name, defType, /* defPackage= */ "android");
+ }
+
+ private static boolean isInputDeviceInfoValid(int id, int axis, int source) {
+ InputDevice device = InputDevice.getDevice(id);
+ return device != null && device.getMotionRange(axis, source) != null;
+ }
}
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
index 3157094..957d459 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
@@ -169,6 +169,11 @@
public static final int TYPE_ASSIST_READING_CONTEXT = 0x01000000;
/**
+ * Represents the event of a scroll having completed and brought the target node on screen.
+ */
+ public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 0x04000000;
+
+ /**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
* The type of change is not defined.
*/
@@ -270,6 +275,7 @@
* @see AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
* @see AccessibilityEvent#TYPE_VIEW_SCROLLED
* @see AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
+ * @see AccessibilityEvent#TYPE_VIEW_TARGETED_BY_SCROLL
* @see #TYPE_ANNOUNCEMENT
* @see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
* @see #TYPE_GESTURE_DETECTION_START
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 3837165..6e63749 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -654,6 +654,40 @@
: null, android.R.id.accessibilityActionShowTextSuggestions, null,
null, null);
+ /**
+ * Action that brings fully on screen the next node in the specified direction.
+ *
+ * <p>
+ * This should include wrapping around to the next/previous row, column, etc. in a
+ * collection if one is available. If there is no node in that direction, the action
+ * should fail and return false.
+ * </p>
+ * <p>
+ * This action should be used instead of
+ * {@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION} when a widget does not
+ * have clear row and column semantics or if a directional search is needed to find a
+ * node in a complex ViewGroup where individual nodes may span multiple rows or
+ * columns. The implementing widget must send a
+ * {@link AccessibilityEventCompat#TYPE_VIEW_TARGETED_BY_SCROLL} accessibility event
+ * with the scroll target as the source. An accessibility service can listen for this
+ * event, inspect its source, and use the result when determining where to place
+ * accessibility focus.
+ * <p>
+ * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_DIRECTION_INT}. This is a
+ * required argument.<br>
+ * </p>
+ */
+ @NonNull
+ @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
+ public static final AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION =
+ new AccessibilityActionCompat(BuildCompat.isAtLeastU()
+ ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION
+ : null,
+ // TODO (267511848): update ID value once U resources are finalized.
+ BuildCompat.isAtLeastU()
+ ? android.R.id.accessibilityActionScrollInDirection : -1,
+ null, null, null);
+
final Object mAction;
private final int mId;
private final Class<? extends CommandArguments> mViewCommandArgumentClass;
@@ -1363,6 +1397,7 @@
private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x00000008;
private static final int BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 5;
+ private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
private final AccessibilityNodeInfo mInfo;
@@ -1751,6 +1786,71 @@
public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
"android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+ /**
+ * <p>Argument to represent the direction when using
+ * {@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}.</p>
+ *
+ * <p>
+ * The value of this argument can be one of:
+ * <ul>
+ * <li>{@link View#FOCUS_DOWN}</li>
+ * <li>{@link View#FOCUS_UP}</li>
+ * <li>{@link View#FOCUS_LEFT}</li>
+ * <li>{@link View#FOCUS_RIGHT}</li>
+ * <li>{@link View#FOCUS_FORWARD}</li>
+ * <li>{@link View#FOCUS_BACKWARD}</li>
+ * </ul>
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_DIRECTION_INT =
+ "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
+
+ /**
+ * <p>Argument to represent the scroll amount as a percent of the visible area of a node, with
+ * 1.0F as the default. Values smaller than 1.0F represent a partial scroll of the node, and
+ * values larger than 1.0F represent a scroll that extends beyond the currently visible node
+ * Rect. Setting this to {@link Float#POSITIVE_INFINITY} or to another "too large" value should
+ * scroll to the end of the node. Negative values should not be used with this argument.
+ * </p>
+ *
+ * <p>
+ * This argument should be used with the following scroll actions:
+ * <ul>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_BACKWARD}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_UP}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_DOWN}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_LEFT}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_RIGHT}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Example: if a view representing a list of items implements
+ * {@link AccessibilityActionCompat#ACTION_SCROLL_FORWARD} to scroll forward by an entire
+ * screen
+ * (one "page"), then passing a value of .25F via this argument should scroll that view
+ * only by 1/4th of a screen. Passing a value of 1.50F via this argument should scroll the
+ * view by 1 1/2 screens or to end of the node if the node doesn't extend to 1 1/2 screens.
+ * </p>
+ *
+ * <p>
+ * This argument should not be used with the following scroll actions, which don't cleanly
+ * conform to granular scroll semantics:
+ * <ul>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}</li>
+ * <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Views that support this argument should set
+ * {@link #setGranularScrollingSupported(boolean)} to true. Clients should use
+ * {@link #isGranularScrollingSupported()} to check if granular scrolling is supported.
+ * </p>
+ */
+ public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
+ "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+
// Focus types
/**
@@ -2730,6 +2830,36 @@
mInfo.setScrollable(scrollable);
}
+
+ /**
+ * Gets if the node supports granular scrolling.
+ *
+ * @return True if all scroll actions that could support
+ * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise.
+ */
+ public boolean isGranularScrollingSupported() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING);
+ }
+
+ /**
+ * Sets if the node supports granular scrolling. This should be set to true if all scroll
+ * actions which could support {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param granularScrollingSupported True if the node supports granular scrolling, false
+ * otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setGranularScrollingSupported(boolean granularScrollingSupported) {
+ setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING,
+ granularScrollingSupported);
+ }
+
/**
* Gets if the node has selectable text.
*
@@ -4609,6 +4739,11 @@
case android.R.id.accessibilityActionDragCancel:
return "ACTION_DRAG_CANCEL";
default:
+ // TODO (b/267511848): fix after Android U constants are finalized.
+ if (Build.VERSION.SDK_INT >= 34
+ && action == android.R.id.accessibilityActionScrollInDirection) {
+ return "ACTION_SCROLL_IN_DIRECTION";
+ }
return "ACTION_UNKNOWN";
}
}
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
index 511d9b8..91586a5 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -87,6 +87,25 @@
return null;
}
+ /**
+ * Creates a new AccessibilityWindowInfoCompat.
+ * <p>
+ * Compatibility:
+ * <ul>
+ * <li>Api < 30: Will not wrap an
+ * {@link android.view.accessibility.AccessibilityWindowInfo} instance.</li>
+ * </ul>
+ * </p>
+ *
+ */
+ public AccessibilityWindowInfoCompat() {
+ if (SDK_INT >= 30) {
+ mInfo = Api30Impl.instantiateAccessibilityWindowInfo();
+ } else {
+ mInfo = null;
+ }
+ }
+
private AccessibilityWindowInfoCompat(Object info) {
mInfo = info;
}
@@ -541,6 +560,18 @@
}
}
+ @RequiresApi(30)
+ private static class Api30Impl {
+ private Api30Impl() {
+ // This class is non instantiable.
+ }
+
+ @DoNotInline
+ static AccessibilityWindowInfo instantiateAccessibilityWindowInfo() {
+ return new AccessibilityWindowInfo();
+ }
+ }
+
@RequiresApi(33)
private static class Api33Impl {
private Api33Impl() {
diff --git a/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java b/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
index a672caa..c5c89d7 100644
--- a/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
+++ b/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
@@ -246,8 +246,7 @@
@DoNotInline
static void notifyViewsAppeared(
ContentCaptureSession contentCaptureSession, List<ViewStructure> appearedNodes) {
- // new API in U
- // contentCaptureSession.notifyViewsAppeared(appearedNodes);
+ contentCaptureSession.notifyViewsAppeared(appearedNodes);
}
}
@RequiresApi(29)
diff --git a/core/core/src/test/resources/robolectric.properties b/core/core/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/core/core/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
index 65dbd50..7c969db 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
@@ -54,7 +54,6 @@
@Test
fun isAvailableOnDevice_apiNotSuccess_returnsFalse() {
- // TODO("If retryables are accounted for, add a third test")
val activityScenario = ActivityScenario.launch(
TestCredentialsActivity::class.java
)
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
index 969f941..45af09f 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
@@ -34,6 +34,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -68,7 +69,7 @@
CredentialProviderBeginSignInController
.getInstance(activity)
.convertRequestToPlayServices(new GetCredentialRequest(List.of(
- new GetPasswordOption(true)
+ new GetPasswordOption(new HashSet<>(), true)
)));
assertThat(actualResponse.getPasswordRequestOptions().isSupported()).isTrue();
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
index a783ea6..e579c2b 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
@@ -66,7 +66,7 @@
.convertRequestToPlayServices(
GetCredentialRequest(
listOf(
- GetPasswordOption(true)
+ GetPasswordOption(isAutoSelectAllowed = true)
)
)
)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
index d4dc2c5..d36bd27 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
@@ -16,7 +16,6 @@
package androidx.credentials.playservices
-import android.app.Activity
import android.content.Context
import android.os.CancellationSignal
import android.util.Log
@@ -54,21 +53,21 @@
@VisibleForTesting
var googleApiAvailability = GoogleApiAvailability.getInstance()
override fun onGetCredential(
+ context: Context,
request: GetCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>
) {
if (cancellationReviewer(cancellationSignal)) { return }
- CredentialProviderBeginSignInController(activity).invokePlayServices(
+ CredentialProviderBeginSignInController(context).invokePlayServices(
request, callback, executor, cancellationSignal)
}
@SuppressWarnings("deprecated")
override fun onCreateCredential(
+ context: Context,
request: CreateCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>
@@ -77,7 +76,7 @@
when (request) {
is CreatePasswordRequest -> {
CredentialProviderCreatePasswordController.getInstance(
- activity).invokePlayServices(
+ context).invokePlayServices(
request,
callback,
executor,
@@ -85,7 +84,7 @@
}
is CreatePublicKeyCredentialRequest -> {
CredentialProviderCreatePublicKeyCredentialController.getInstance(
- activity).invokePlayServices(
+ context).invokePlayServices(
request,
callback,
executor,
@@ -104,8 +103,9 @@
}
// https://developers.google.com/android/reference/com/google/android/gms/common/ConnectionResult
- // TODO(b/262924507): Most codes indicate failure, but two indicate retry-ability -
- // look into handling.
+ // There is one error code that supports retry API_DISABLED_FOR_CONNECTION but it would not
+ // be useful to retry that one because our connection to GMSCore is a static variable
+ // (see GoogleApiAvailability.getInstance()) so we cannot recreate the connection to retry.
private fun isGooglePlayServicesAvailable(context: Context): Int {
return googleApiAvailability.isGooglePlayServicesAvailable(context)
}
@@ -116,33 +116,23 @@
executor: Executor,
callback: CredentialManagerCallback<Void?, ClearCredentialException>
) {
- if (cancellationReviewer(cancellationSignal)) {
- return
- }
+ if (cancellationReviewer(cancellationSignal)) { return }
Identity.getSignInClient(context)
.signOut()
.addOnSuccessListener {
- var isCanceled = false
- cancellationSignal?.let {
- isCanceled = cancellationSignal.isCanceled
- }
- if (!isCanceled) {
+ cancellationReviewerWithCallback(cancellationSignal, {
Log.i(TAG, "During clear credential, signed out successfully!")
executor.execute { callback.onResult(null) }
- }
+ })
}
.addOnFailureListener { e ->
run {
- var isCanceled = false
- cancellationSignal?.let {
- isCanceled = cancellationSignal.isCanceled
- }
- if (!isCanceled) {
+ cancellationReviewerWithCallback(cancellationSignal, {
Log.w(TAG, "During clear credential sign out failed with $e")
executor.execute {
callback.onError(ClearCredentialUnknownException(e.message))
}
- }
+ })
}
}
}
@@ -150,14 +140,21 @@
companion object {
private val TAG = CredentialProviderPlayServicesImpl::class.java.name
+ internal fun cancellationReviewerWithCallback(
+ cancellationSignal: CancellationSignal?,
+ callback: () -> Unit,
+ ) {
+ if (!cancellationReviewer(cancellationSignal)) {
+ callback()
+ }
+ }
+
internal fun cancellationReviewer(
cancellationSignal: CancellationSignal?
): Boolean {
if (cancellationSignal != null) {
if (cancellationSignal.isCanceled) {
Log.i(TAG, "the flow has been canceled")
- // TODO(b/262924507): See if there's a better way to message pass to avoid
- // if statements and to use a single listener instead
return true
}
} else {
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
index cf5f665..fc59657 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
@@ -16,28 +16,16 @@
package androidx.credentials.playservices.controllers.BeginSignIn
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+import android.content.Context
+import android.content.pm.PackageManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility.Companion.convertToPlayAuthPasskeyJsonRequest
+import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility.Companion.convertToPlayAuthPasskeyRequest
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.BeginSignInRequest.GoogleIdTokenRequestOptions
+import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
/**
@@ -45,12 +33,14 @@
*
* @hide
*/
+@Suppress("deprecation")
class BeginSignInControllerUtility {
companion object {
private val TAG = BeginSignInControllerUtility::class.java.name
- internal fun constructBeginSignInRequest(request: GetCredentialRequest):
+ private const val AUTH_MIN_VERSION_JSON_PARSING: Long = 231815000
+ internal fun constructBeginSignInRequest(request: GetCredentialRequest, context: Context):
BeginSignInRequest {
var isPublicKeyCredReqFound = false
val requestBuilder = BeginSignInRequest.Builder()
@@ -64,15 +54,23 @@
)
autoSelect = autoSelect || option.isAutoSelectAllowed
} else if (option is GetPublicKeyCredentialOption && !isPublicKeyCredReqFound) {
- requestBuilder.setPasskeyJsonSignInRequestOptions(
- convertToPlayAuthPasskeyJsonRequest(option)
- )
+ val curAuthVersion = determineDeviceGMSVersionCode(context)
+ if (needsBackwardsCompatibleRequest(curAuthVersion)) {
+ requestBuilder.setPasskeysSignInRequestOptions(
+ convertToPlayAuthPasskeyRequest(option)
+ )
+ } else {
+ requestBuilder.setPasskeyJsonSignInRequestOptions(
+ convertToPlayAuthPasskeyJsonRequest(option)
+ )
+ }
isPublicKeyCredReqFound = true
// TODO(b/262924507) : watch for GIS update on single vs multiple options of a
// single type. Also make allow list update as GIS has done it.
} else if (option is GetGoogleIdOption) {
requestBuilder.setGoogleIdTokenRequestOptions(
- convertToGoogleIdTokenOption(option))
+ convertToGoogleIdTokenOption(option)
+ )
autoSelect = autoSelect || option.autoSelectEnabled
}
}
@@ -81,8 +79,31 @@
.build()
}
+ /**
+ * Recovers the current GMS version code *running on the device*. This is needed because
+ * even if a dependency knows the methods and functions of a newer code, the device may
+ * only contain the older module, which can cause exceptions due to the discrepancy.
+ */
+ private fun determineDeviceGMSVersionCode(context: Context): Long {
+ val packageManager: PackageManager = context.packageManager
+ val packageName = GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE
+ return packageManager.getPackageInfo(packageName, 0).versionCode.toLong()
+ }
+
+ /**
+ * Determines if curAuthVersion needs the backwards compatible GIS json parsing flow or
+ * not. If curAuthVersion >= minVersion for the new flow, this returns false.
+ * Otherwise, it's < than the minVersion for the new flow, so this is true.
+ */
+ private fun needsBackwardsCompatibleRequest(curAuthVersion: Long): Boolean {
+ if (curAuthVersion >= AUTH_MIN_VERSION_JSON_PARSING) {
+ return false
+ }
+ return true
+ }
+
private fun convertToGoogleIdTokenOption(option: GetGoogleIdOption):
- GoogleIdTokenRequestOptions {
+ GoogleIdTokenRequestOptions {
var idTokenOption =
GoogleIdTokenRequestOptions.builder()
.setFilterByAuthorizedAccounts(option.filterByAuthorizedAccounts)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index dcea304..cab317b 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -16,7 +16,7 @@
package androidx.credentials.playservices.controllers.BeginSignIn
-import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.CancellationSignal
@@ -55,13 +55,13 @@
* @hide
*/
@Suppress("deprecation")
-class CredentialProviderBeginSignInController(private val activity: Activity) :
+class CredentialProviderBeginSignInController(private val context: Context) :
CredentialProviderController<
GetCredentialRequest,
BeginSignInRequest,
SignInCredential,
GetCredentialResponse,
- GetCredentialException>(activity) {
+ GetCredentialException>(context) {
/**
* The callback object state, used in the protected handleResponse method.
@@ -119,10 +119,10 @@
}
val convertedRequest: BeginSignInRequest = this.convertRequestToPlayServices(request)
- val hiddenIntent = Intent(activity, HiddenActivity::class.java)
+ val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent, BEGIN_SIGN_IN_TAG)
- activity.startActivity(hiddenIntent)
+ context.startActivity(hiddenIntent)
}
internal fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
@@ -143,7 +143,7 @@
)
) return
try {
- val signInCredential = Identity.getSignInClient(activity)
+ val signInCredential = Identity.getSignInClient(context)
.getSignInCredentialFromIntent(data)
val response = convertResponseToCredentialManager(signInCredential)
cancelOrCallbackExceptionOrResult(cancellationSignal) {
@@ -183,7 +183,7 @@
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override fun convertRequestToPlayServices(request: GetCredentialRequest):
BeginSignInRequest {
- return constructBeginSignInRequest(request)
+ return constructBeginSignInRequest(request, context)
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
@@ -246,14 +246,14 @@
* This finds a past version of the [CredentialProviderBeginSignInController] if it exists,
* otherwise it generates a new instance.
*
- * @param activity the calling activity for this controller
+ * @param context the calling context for this controller
* @return a credential provider controller for a specific begin sign in credential request
*/
@JvmStatic
- fun getInstance(activity: Activity):
+ fun getInstance(context: Context):
CredentialProviderBeginSignInController {
if (controller == null) {
- controller = CredentialProviderBeginSignInController(activity)
+ controller = CredentialProviderBeginSignInController(context)
}
return controller!!
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
index a7b6746..9c4d131 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -16,7 +16,7 @@
package androidx.credentials.playservices.controllers.CreatePassword
-import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.CancellationSignal
@@ -44,13 +44,13 @@
* @hide
*/
@Suppress("deprecation")
-class CredentialProviderCreatePasswordController(private val activity: Activity) :
+class CredentialProviderCreatePasswordController(private val context: Context) :
CredentialProviderController<
CreatePasswordRequest,
SavePasswordRequest,
Unit,
CreateCredentialResponse,
- CreateCredentialException>(activity) {
+ CreateCredentialException>(context) {
/**
* The callback object state, used in the protected handleResponse method.
@@ -101,10 +101,10 @@
}
val convertedRequest: SavePasswordRequest = this.convertRequestToPlayServices(request)
- val hiddenIntent = Intent(activity, HiddenActivity::class.java)
+ val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent, CREATE_PASSWORD_TAG)
- activity.startActivity(hiddenIntent)
+ context.startActivity(hiddenIntent)
}
internal fun handleResponse(uniqueRequestCode: Int, resultCode: Int) {
@@ -144,14 +144,14 @@
* [CredentialProviderCreatePasswordController] if it exists, otherwise
* it generates a new instance.
*
- * @param activity the calling activity for this controller
+ * @param context the calling context for this controller
* @return a credential provider controller for CreatePasswordController
*/
@JvmStatic
- fun getInstance(activity: Activity):
+ fun getInstance(context: Context):
CredentialProviderCreatePasswordController {
if (controller == null) {
- controller = CredentialProviderCreatePasswordController(activity)
+ controller = CredentialProviderCreatePasswordController(context)
}
return controller!!
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index ac236eb..110df63 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -16,7 +16,7 @@
package androidx.credentials.playservices.controllers.CreatePublicKeyCredential
-import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.CancellationSignal
@@ -50,13 +50,13 @@
* @hide
*/
@Suppress("deprecation")
-class CredentialProviderCreatePublicKeyCredentialController(private val activity: Activity) :
+class CredentialProviderCreatePublicKeyCredentialController(private val context: Context) :
CredentialProviderController<
CreatePublicKeyCredentialRequest,
PublicKeyCredentialCreationOptions,
PublicKeyCredential,
CreateCredentialResponse,
- CreateCredentialException>(activity) {
+ CreateCredentialException>(context) {
/**
* The callback object state, used in the protected handleResponse method.
@@ -121,11 +121,11 @@
if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
return
}
- val hiddenIntent = Intent(activity, HiddenActivity::class.java)
+ val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, fidoRegistrationRequest)
generateHiddenActivityIntent(resultReceiver, hiddenIntent,
CREATE_PUBLIC_KEY_CREDENTIAL_TAG)
- activity.startActivity(hiddenIntent)
+ context.startActivity(hiddenIntent)
}
internal fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
@@ -196,14 +196,14 @@
* [CredentialProviderCreatePublicKeyCredentialController] if it exists, otherwise
* it generates a new instance.
*
- * @param activity the calling activity for this controller
+ * @param context the calling context for this controller
* @return a credential provider controller for CreatePublicKeyCredential
*/
@JvmStatic
- fun getInstance(activity: Activity):
+ fun getInstance(context: Context):
CredentialProviderCreatePublicKeyCredentialController {
if (controller == null) {
- controller = CredentialProviderCreatePublicKeyCredentialController(activity)
+ controller = CredentialProviderCreatePublicKeyCredentialController(context)
}
return controller!!
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index 1d1ffc2..75f09f6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -241,6 +241,7 @@
* Converts from the Credential Manager public key credential option to the Play Auth
* Module passkey json option.
*
+ * @return the current auth module passkey request
*/
fun convertToPlayAuthPasskeyJsonRequest(option: GetPublicKeyCredentialOption):
BeginSignInRequest.PasskeyJsonRequestOptions {
@@ -250,6 +251,30 @@
.build()
}
+ /**
+ * Converts from the Credential Manager public key credential option to the Play Auth
+ * Module passkey option, used in a backwards compatible flow for the auth dependency.
+ *
+ * @return the backwards compatible auth module passkey request
+ */
+ @Deprecated("Upgrade GMS version so 'convertToPlayAuthPasskeyJsoNRequest' is used")
+ @Suppress("deprecation")
+ fun convertToPlayAuthPasskeyRequest(option: GetPublicKeyCredentialOption):
+ BeginSignInRequest.PasskeysRequestOptions {
+ val json = JSONObject(option.requestJson)
+ val rpId = json.optString("rpId", "")
+ if (rpId.isEmpty()) {
+ throw JSONException("GetPublicKeyCredentialOption - rpId not specified in the " +
+ "request or is unexpectedly empty")
+ }
+ val challenge = getChallenge(json)
+ return BeginSignInRequest.PasskeysRequestOptions.Builder()
+ .setSupported(true)
+ .setRpId(rpId)
+ .setChallenge(challenge)
+ .build()
+ }
+
private fun getChallenge(json: JSONObject): ByteArray {
val challengeB64 = json.optString("challenge", "")
if (challengeB64.isEmpty()) {
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderBaseController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderBaseController.kt
index 7e9365d..03a254c 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderBaseController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderBaseController.kt
@@ -16,6 +16,7 @@
package androidx.credentials.playservices.controllers
+import android.content.Context
import android.content.Intent
import android.os.Parcel
import android.os.ResultReceiver
@@ -34,7 +35,7 @@
* Holds all non type specific details shared by the controllers.
* @hide
*/
-open class CredentialProviderBaseController(private val activity: android.app.Activity) {
+open class CredentialProviderBaseController(private val context: Context) {
companion object {
// Common retryable status codes from the play modules found
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
index fafe63a..8a5dac6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
@@ -17,6 +17,7 @@
package androidx.credentials.playservices.controllers
import android.app.Activity
+import android.content.Context
import android.os.Bundle
import android.os.CancellationSignal
import androidx.credentials.CredentialManagerCallback
@@ -45,7 +46,7 @@
*/
@Suppress("deprecation")
abstract class CredentialProviderController<T1 : Any, T2 : Any, R2 : Any, R1 : Any,
- E1 : Any>(private val activity: Activity) : CredentialProviderBaseController(activity) {
+ E1 : Any>(private val context: Context) : CredentialProviderBaseController(context) {
companion object {
diff --git a/credentials/credentials/api/api_lint.ignore b/credentials/credentials/api/api_lint.ignore
index be1a990..8d0b1d6 100644
--- a/credentials/credentials/api/api_lint.ignore
+++ b/credentials/credentials/api/api_lint.ignore
@@ -1,5 +1,7 @@
// Baseline format: 1.0
-GetterSetterNames: field CreatePublicKeyCredentialRequest.preferImmediatelyAvailableCredentials:
+GetterSetterNames: field CreateCredentialRequest.preferImmediatelyAvailableCredentials:
Invalid name for boolean property `preferImmediatelyAvailableCredentials`. Should start with one of `has`, `can`, `should`, `is`.
-GetterSetterNames: field GetPublicKeyCredentialOption.preferImmediatelyAvailableCredentials:
+GetterSetterNames: field GetCredentialRequest.preferIdentityDocUi:
+ Invalid name for boolean property `preferIdentityDocUi`. Should start with one of `has`, `can`, `should`, `is`.
+GetterSetterNames: field GetCredentialRequest.preferImmediatelyAvailableCredentials:
Invalid name for boolean property `preferImmediatelyAvailableCredentials`. Should start with one of `has`, `can`, `should`, `is`.
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index 5f71623..a9d3faf 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -6,13 +6,28 @@
}
public abstract class CreateCredentialRequest {
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final android.os.Bundle getCredentialData();
+ method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
method public final String? getOrigin();
+ method public final boolean getPreferImmediatelyAvailableCredentials();
+ method public final String getType();
+ method public final boolean isAutoSelectAllowed();
+ method public final boolean isSystemProviderRequired();
+ property public final android.os.Bundle candidateQueryData;
+ property public final android.os.Bundle credentialData;
+ property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
+ property public final boolean isAutoSelectAllowed;
+ property public final boolean isSystemProviderRequired;
property public final String? origin;
+ property public final boolean preferImmediatelyAvailableCredentials;
+ property public final String type;
}
public static final class CreateCredentialRequest.DisplayInfo {
ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
+ ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
method public CharSequence? getUserDisplayName();
method public CharSequence getUserId();
property public final CharSequence? userDisplayName;
@@ -20,35 +35,28 @@
}
public abstract class CreateCredentialResponse {
- }
-
- public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
- method public final android.os.Bundle getCandidateQueryData();
- method public final android.os.Bundle getCredentialData();
- method public final String getType();
- method public final boolean isAutoSelectAllowed();
- method public final boolean isSystemProviderRequired();
- property public final android.os.Bundle candidateQueryData;
- property public final android.os.Bundle credentialData;
- property public final boolean isAutoSelectAllowed;
- property public final boolean isSystemProviderRequired;
- property public final String type;
- }
-
- public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
- ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
method public final android.os.Bundle getData();
method public final String getType();
property public final android.os.Bundle data;
property public final String type;
}
+ public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
+ }
+
+ public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
+ ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
+ }
+
public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
ctor public CreatePasswordRequest(String id, String password);
ctor public CreatePasswordRequest(String id, String password, optional String? origin);
+ ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
+ ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials);
method public String getId();
method public String getPassword();
property public final String id;
@@ -61,14 +69,13 @@
public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
ctor public CreatePublicKeyCredentialRequest(String requestJson);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
- method public String? getClientDataHash();
- method public boolean getPreferImmediatelyAvailableCredentials();
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider);
+ method public byte[]? getClientDataHash();
method public String getRequestJson();
- property public final String? clientDataHash;
- property public final boolean preferImmediatelyAvailableCredentials;
+ property public final byte[]? clientDataHash;
property public final String requestJson;
}
@@ -79,16 +86,25 @@
}
public abstract class Credential {
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
- public final class CredentialManager {
- method public suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ @RequiresApi(16) public interface CredentialManager {
+ method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
- method public static androidx.credentials.CredentialManager create(android.content.Context context);
- method public suspend Object? createCredential(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
- method public void createCredentialAsync(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
- method public suspend Object? getCredential(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
- method public void getCredentialAsync(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public default static androidx.credentials.CredentialManager create(android.content.Context context);
+ method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
+ method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
+ method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
+ method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
+ method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
+ method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
field public static final androidx.credentials.CredentialManager.Companion Companion;
}
@@ -102,30 +118,49 @@
}
public abstract class CredentialOption {
+ method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final android.os.Bundle getRequestData();
+ method public final String getType();
+ method public final boolean isAutoSelectAllowed();
+ method public final boolean isSystemProviderRequired();
+ property public final java.util.Set<android.content.ComponentName> allowedProviders;
+ property public final android.os.Bundle candidateQueryData;
+ property public final boolean isAutoSelectAllowed;
+ property public final boolean isSystemProviderRequired;
+ property public final android.os.Bundle requestData;
+ property public final String type;
}
public interface CredentialProvider {
method public boolean isAvailableOnDevice();
method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
- method public void onCreateCredential(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
- method public void onGetCredential(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
}
public class CustomCredential extends androidx.credentials.Credential {
ctor public CustomCredential(String type, android.os.Bundle data);
- method public final android.os.Bundle getData();
- method public final String getType();
- property public final android.os.Bundle data;
- property public final String type;
}
public final class GetCredentialRequest {
ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
method public String? getOrigin();
+ method public boolean getPreferIdentityDocUi();
+ method public boolean getPreferImmediatelyAvailableCredentials();
+ method public android.content.ComponentName? getPreferUiBrandingComponentName();
property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
property public final String? origin;
+ property public final boolean preferIdentityDocUi;
+ property public final boolean preferImmediatelyAvailableCredentials;
+ property public final android.content.ComponentName? preferUiBrandingComponentName;
}
public static final class GetCredentialRequest.Builder {
@@ -134,6 +169,9 @@
method public androidx.credentials.GetCredentialRequest build();
method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
}
public final class GetCredentialResponse {
@@ -145,34 +183,25 @@
public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
- method public final android.os.Bundle getCandidateQueryData();
- method public final android.os.Bundle getRequestData();
- method public final String getType();
- method public final boolean isAutoSelectAllowed();
- method public final boolean isSystemProviderRequired();
- property public final android.os.Bundle candidateQueryData;
- property public final boolean isAutoSelectAllowed;
- property public final boolean isSystemProviderRequired;
- property public final android.os.Bundle requestData;
- property public final String type;
+ ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
}
public final class GetPasswordOption extends androidx.credentials.CredentialOption {
ctor public GetPasswordOption();
- ctor public GetPasswordOption(optional boolean isAutoSelectAllowed);
- method public boolean isAutoSelectAllowed();
- property public boolean isAutoSelectAllowed;
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
+ method public java.util.Set<java.lang.String> getAllowedUserIds();
+ property public final java.util.Set<java.lang.String> allowedUserIds;
}
public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
ctor public GetPublicKeyCredentialOption(String requestJson);
- ctor public GetPublicKeyCredentialOption(String requestJson, optional String? clientDataHash);
- ctor public GetPublicKeyCredentialOption(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
- method public String? getClientDataHash();
- method public boolean getPreferImmediatelyAvailableCredentials();
+ ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
+ ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
+ method public byte[]? getClientDataHash();
method public String getRequestJson();
- property public final String? clientDataHash;
- property public final boolean preferImmediatelyAvailableCredentials;
+ property public final byte[]? clientDataHash;
property public final String requestJson;
}
@@ -182,12 +211,34 @@
method public String getPassword();
property public final String id;
property public final String password;
+ field public static final androidx.credentials.PasswordCredential.Companion Companion;
+ field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
+ }
+
+ public static final class PasswordCredential.Companion {
+ }
+
+ @RequiresApi(34) public final class PrepareGetCredentialResponse {
+ method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle getPendingGetCredentialHandle();
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
+ property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle;
+ }
+
+ @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
+ ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
}
public final class PublicKeyCredential extends androidx.credentials.Credential {
ctor public PublicKeyCredential(String authenticationResponseJson);
method public String getAuthenticationResponseJson();
property public final String authenticationResponseJson;
+ field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
+ field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
+ }
+
+ public static final class PublicKeyCredential.Companion {
}
}
@@ -454,3 +505,349 @@
}
+package androidx.credentials.provider {
+
+ public final class Action {
+ ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence? getSubtitle();
+ method public CharSequence getTitle();
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence? subtitle;
+ property public final CharSequence title;
+ }
+
+ public static final class Action.Builder {
+ ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
+ method public androidx.credentials.provider.Action build();
+ method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
+ }
+
+ public final class AuthenticationAction {
+ ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTitle();
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence title;
+ }
+
+ public abstract class BeginCreateCredentialRequest {
+ ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, android.service.credentials.CallingAppInfo? callingAppInfo);
+ method public final android.service.credentials.CallingAppInfo? getCallingAppInfo();
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final String getType();
+ method public static final androidx.credentials.provider.BeginCreateCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public static final android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
+ property public final android.service.credentials.CallingAppInfo? callingAppInfo;
+ property public final android.os.Bundle candidateQueryData;
+ property public final String type;
+ field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
+ }
+
+ public static final class BeginCreateCredentialRequest.Companion {
+ method public androidx.credentials.provider.BeginCreateCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
+ }
+
+ public final class BeginCreateCredentialResponse {
+ ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
+ method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
+ method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
+ method public static androidx.credentials.provider.BeginCreateCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
+ property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
+ property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
+ field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
+ }
+
+ public static final class BeginCreateCredentialResponse.Builder {
+ ctor public BeginCreateCredentialResponse.Builder();
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
+ method public androidx.credentials.provider.BeginCreateCredentialResponse build();
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
+ }
+
+ public static final class BeginCreateCredentialResponse.Companion {
+ method public androidx.credentials.provider.BeginCreateCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
+ }
+
+ public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, android.service.credentials.CallingAppInfo? callingAppInfo);
+ }
+
+ public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreatePasswordCredentialRequest(android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
+ }
+
+ public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
+ ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
+ method public byte[]? getClientDataHash();
+ method public String getRequestJson();
+ property public final byte[]? clientDataHash;
+ property public final String requestJson;
+ }
+
+ public abstract class BeginGetCredentialOption {
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final String getId();
+ method public final String getType();
+ property public final android.os.Bundle candidateQueryData;
+ property public final String id;
+ property public final String type;
+ }
+
+ public final class BeginGetCredentialRequest {
+ ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
+ ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional android.service.credentials.CallingAppInfo? callingAppInfo);
+ method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
+ method public android.service.credentials.CallingAppInfo? getCallingAppInfo();
+ method public static androidx.credentials.provider.BeginGetCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
+ property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
+ property public final android.service.credentials.CallingAppInfo? callingAppInfo;
+ field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
+ }
+
+ public static final class BeginGetCredentialRequest.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
+ }
+
+ public final class BeginGetCredentialResponse {
+ ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
+ method public java.util.List<androidx.credentials.provider.Action> getActions();
+ method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
+ method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
+ method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
+ method public static androidx.credentials.provider.BeginGetCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
+ property public final java.util.List<androidx.credentials.provider.Action> actions;
+ property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
+ property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
+ property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
+ field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
+ }
+
+ public static final class BeginGetCredentialResponse.Builder {
+ ctor public BeginGetCredentialResponse.Builder();
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
+ method public androidx.credentials.provider.BeginGetCredentialResponse build();
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
+ }
+
+ public static final class BeginGetCredentialResponse.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
+ }
+
+ public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
+ }
+
+ public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
+ method public java.util.Set<java.lang.String> getAllowedUserIds();
+ property public final java.util.Set<java.lang.String> allowedUserIds;
+ }
+
+ public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
+ ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
+ method public byte[]? getClientDataHash();
+ method public String getRequestJson();
+ property public final byte[]? clientDataHash;
+ property public final String requestJson;
+ }
+
+ @RequiresApi(28) public final class CreateEntry {
+ ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
+ method public CharSequence getAccountName();
+ method public CharSequence? getDescription();
+ method public android.graphics.drawable.Icon? getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public Integer? getPasswordCredentialCount();
+ method public android.app.PendingIntent getPendingIntent();
+ method public Integer? getPublicKeyCredentialCount();
+ method public Integer? getTotalCredentialCount();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence accountName;
+ property public final CharSequence? description;
+ property public final android.graphics.drawable.Icon? icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ }
+
+ public static final class CreateEntry.Builder {
+ ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
+ method public androidx.credentials.provider.CreateEntry build();
+ method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
+ method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
+ method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
+ method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
+ method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
+ }
+
+ public abstract class CredentialEntry {
+ method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+ property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
+ }
+
+ @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
+ ctor public CredentialProviderService();
+ method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
+ method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
+ method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
+ method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
+ }
+
+ @RequiresApi(28) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence? getSubtitle();
+ method public CharSequence getTitle();
+ method public String getType();
+ method public CharSequence? getTypeDisplayName();
+ method public boolean isAutoSelectAllowed();
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence? subtitle;
+ property public final CharSequence title;
+ property public String type;
+ property public final CharSequence? typeDisplayName;
+ }
+
+ public static final class CustomCredentialEntry.Builder {
+ ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
+ method public androidx.credentials.provider.CustomCredentialEntry build();
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
+ }
+
+ @RequiresApi(28) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public CharSequence? getDisplayName();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTypeDisplayName();
+ method public CharSequence getUsername();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence? displayName;
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence typeDisplayName;
+ property public final CharSequence username;
+ }
+
+ public static final class PasswordCredentialEntry.Builder {
+ ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
+ method public androidx.credentials.provider.PasswordCredentialEntry build();
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ }
+
+ @RequiresApi(34) public final class PendingIntentHandler {
+ ctor public PendingIntentHandler();
+ method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
+ method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
+ method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
+ method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
+ method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
+ method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
+ method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
+ method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
+ field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
+ }
+
+ public static final class PendingIntentHandler.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
+ method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
+ method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
+ method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
+ method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
+ method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
+ method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
+ method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
+ }
+
+ public final class ProviderClearCredentialStateRequest {
+ ctor public ProviderClearCredentialStateRequest(android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ }
+
+ public final class ProviderCreateCredentialRequest {
+ ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ method public androidx.credentials.CreateCredentialRequest getCallingRequest();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ property public final androidx.credentials.CreateCredentialRequest callingRequest;
+ }
+
+ @RequiresApi(34) public final class ProviderGetCredentialRequest {
+ ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
+ }
+
+ @RequiresApi(28) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public CharSequence? getDisplayName();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTypeDisplayName();
+ method public CharSequence getUsername();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence? displayName;
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence typeDisplayName;
+ property public final CharSequence username;
+ }
+
+ public static final class PublicKeyCredentialEntry.Builder {
+ ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry build();
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ }
+
+ public final class RemoteEntry {
+ ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
+ method public android.app.PendingIntent getPendingIntent();
+ property public final android.app.PendingIntent pendingIntent;
+ }
+
+}
+
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index 5f71623..a9d3faf 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -6,13 +6,28 @@
}
public abstract class CreateCredentialRequest {
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final android.os.Bundle getCredentialData();
+ method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
method public final String? getOrigin();
+ method public final boolean getPreferImmediatelyAvailableCredentials();
+ method public final String getType();
+ method public final boolean isAutoSelectAllowed();
+ method public final boolean isSystemProviderRequired();
+ property public final android.os.Bundle candidateQueryData;
+ property public final android.os.Bundle credentialData;
+ property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
+ property public final boolean isAutoSelectAllowed;
+ property public final boolean isSystemProviderRequired;
property public final String? origin;
+ property public final boolean preferImmediatelyAvailableCredentials;
+ property public final String type;
}
public static final class CreateCredentialRequest.DisplayInfo {
ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
+ ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
method public CharSequence? getUserDisplayName();
method public CharSequence getUserId();
property public final CharSequence? userDisplayName;
@@ -20,35 +35,28 @@
}
public abstract class CreateCredentialResponse {
- }
-
- public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
- ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
- method public final android.os.Bundle getCandidateQueryData();
- method public final android.os.Bundle getCredentialData();
- method public final String getType();
- method public final boolean isAutoSelectAllowed();
- method public final boolean isSystemProviderRequired();
- property public final android.os.Bundle candidateQueryData;
- property public final android.os.Bundle credentialData;
- property public final boolean isAutoSelectAllowed;
- property public final boolean isSystemProviderRequired;
- property public final String type;
- }
-
- public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
- ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
method public final android.os.Bundle getData();
method public final String getType();
property public final android.os.Bundle data;
property public final String type;
}
+ public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
+ ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
+ }
+
+ public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
+ ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
+ }
+
public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
ctor public CreatePasswordRequest(String id, String password);
ctor public CreatePasswordRequest(String id, String password, optional String? origin);
+ ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
+ ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials);
method public String getId();
method public String getPassword();
property public final String id;
@@ -61,14 +69,13 @@
public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
ctor public CreatePublicKeyCredentialRequest(String requestJson);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
- ctor public CreatePublicKeyCredentialRequest(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
- method public String? getClientDataHash();
- method public boolean getPreferImmediatelyAvailableCredentials();
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
+ ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider);
+ method public byte[]? getClientDataHash();
method public String getRequestJson();
- property public final String? clientDataHash;
- property public final boolean preferImmediatelyAvailableCredentials;
+ property public final byte[]? clientDataHash;
property public final String requestJson;
}
@@ -79,16 +86,25 @@
}
public abstract class Credential {
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
- public final class CredentialManager {
- method public suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ @RequiresApi(16) public interface CredentialManager {
+ method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
- method public static androidx.credentials.CredentialManager create(android.content.Context context);
- method public suspend Object? createCredential(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
- method public void createCredentialAsync(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
- method public suspend Object? getCredential(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
- method public void getCredentialAsync(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public default static androidx.credentials.CredentialManager create(android.content.Context context);
+ method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
+ method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
+ method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
+ method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
+ method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
+ method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
field public static final androidx.credentials.CredentialManager.Companion Companion;
}
@@ -102,30 +118,49 @@
}
public abstract class CredentialOption {
+ method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final android.os.Bundle getRequestData();
+ method public final String getType();
+ method public final boolean isAutoSelectAllowed();
+ method public final boolean isSystemProviderRequired();
+ property public final java.util.Set<android.content.ComponentName> allowedProviders;
+ property public final android.os.Bundle candidateQueryData;
+ property public final boolean isAutoSelectAllowed;
+ property public final boolean isSystemProviderRequired;
+ property public final android.os.Bundle requestData;
+ property public final String type;
}
public interface CredentialProvider {
method public boolean isAvailableOnDevice();
method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
- method public void onCreateCredential(androidx.credentials.CreateCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
- method public void onGetCredential(androidx.credentials.GetCredentialRequest request, android.app.Activity activity, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
}
public class CustomCredential extends androidx.credentials.Credential {
ctor public CustomCredential(String type, android.os.Bundle data);
- method public final android.os.Bundle getData();
- method public final String getType();
- property public final android.os.Bundle data;
- property public final String type;
}
public final class GetCredentialRequest {
ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
+ ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
method public String? getOrigin();
+ method public boolean getPreferIdentityDocUi();
+ method public boolean getPreferImmediatelyAvailableCredentials();
+ method public android.content.ComponentName? getPreferUiBrandingComponentName();
property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
property public final String? origin;
+ property public final boolean preferIdentityDocUi;
+ property public final boolean preferImmediatelyAvailableCredentials;
+ property public final android.content.ComponentName? preferUiBrandingComponentName;
}
public static final class GetCredentialRequest.Builder {
@@ -134,6 +169,9 @@
method public androidx.credentials.GetCredentialRequest build();
method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
+ method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
}
public final class GetCredentialResponse {
@@ -145,34 +183,25 @@
public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
- method public final android.os.Bundle getCandidateQueryData();
- method public final android.os.Bundle getRequestData();
- method public final String getType();
- method public final boolean isAutoSelectAllowed();
- method public final boolean isSystemProviderRequired();
- property public final android.os.Bundle candidateQueryData;
- property public final boolean isAutoSelectAllowed;
- property public final boolean isSystemProviderRequired;
- property public final android.os.Bundle requestData;
- property public final String type;
+ ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
}
public final class GetPasswordOption extends androidx.credentials.CredentialOption {
ctor public GetPasswordOption();
- ctor public GetPasswordOption(optional boolean isAutoSelectAllowed);
- method public boolean isAutoSelectAllowed();
- property public boolean isAutoSelectAllowed;
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
+ ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
+ method public java.util.Set<java.lang.String> getAllowedUserIds();
+ property public final java.util.Set<java.lang.String> allowedUserIds;
}
public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
ctor public GetPublicKeyCredentialOption(String requestJson);
- ctor public GetPublicKeyCredentialOption(String requestJson, optional String? clientDataHash);
- ctor public GetPublicKeyCredentialOption(String requestJson, optional String? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
- method public String? getClientDataHash();
- method public boolean getPreferImmediatelyAvailableCredentials();
+ ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
+ ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
+ method public byte[]? getClientDataHash();
method public String getRequestJson();
- property public final String? clientDataHash;
- property public final boolean preferImmediatelyAvailableCredentials;
+ property public final byte[]? clientDataHash;
property public final String requestJson;
}
@@ -182,12 +211,34 @@
method public String getPassword();
property public final String id;
property public final String password;
+ field public static final androidx.credentials.PasswordCredential.Companion Companion;
+ field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
+ }
+
+ public static final class PasswordCredential.Companion {
+ }
+
+ @RequiresApi(34) public final class PrepareGetCredentialResponse {
+ method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle getPendingGetCredentialHandle();
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
+ method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
+ property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle;
+ }
+
+ @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
+ ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
}
public final class PublicKeyCredential extends androidx.credentials.Credential {
ctor public PublicKeyCredential(String authenticationResponseJson);
method public String getAuthenticationResponseJson();
property public final String authenticationResponseJson;
+ field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
+ field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
+ }
+
+ public static final class PublicKeyCredential.Companion {
}
}
@@ -454,3 +505,349 @@
}
+package androidx.credentials.provider {
+
+ public final class Action {
+ ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence? getSubtitle();
+ method public CharSequence getTitle();
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence? subtitle;
+ property public final CharSequence title;
+ }
+
+ public static final class Action.Builder {
+ ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
+ method public androidx.credentials.provider.Action build();
+ method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
+ }
+
+ public final class AuthenticationAction {
+ ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTitle();
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence title;
+ }
+
+ public abstract class BeginCreateCredentialRequest {
+ ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, android.service.credentials.CallingAppInfo? callingAppInfo);
+ method public final android.service.credentials.CallingAppInfo? getCallingAppInfo();
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final String getType();
+ method public static final androidx.credentials.provider.BeginCreateCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public static final android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
+ property public final android.service.credentials.CallingAppInfo? callingAppInfo;
+ property public final android.os.Bundle candidateQueryData;
+ property public final String type;
+ field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
+ }
+
+ public static final class BeginCreateCredentialRequest.Companion {
+ method public androidx.credentials.provider.BeginCreateCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
+ }
+
+ public final class BeginCreateCredentialResponse {
+ ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
+ method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
+ method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
+ method public static androidx.credentials.provider.BeginCreateCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
+ property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
+ property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
+ field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
+ }
+
+ public static final class BeginCreateCredentialResponse.Builder {
+ ctor public BeginCreateCredentialResponse.Builder();
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
+ method public androidx.credentials.provider.BeginCreateCredentialResponse build();
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
+ method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
+ }
+
+ public static final class BeginCreateCredentialResponse.Companion {
+ method public androidx.credentials.provider.BeginCreateCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
+ }
+
+ public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, android.service.credentials.CallingAppInfo? callingAppInfo);
+ }
+
+ public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreatePasswordCredentialRequest(android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
+ }
+
+ public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
+ ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
+ ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, android.service.credentials.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
+ method public byte[]? getClientDataHash();
+ method public String getRequestJson();
+ property public final byte[]? clientDataHash;
+ property public final String requestJson;
+ }
+
+ public abstract class BeginGetCredentialOption {
+ method public final android.os.Bundle getCandidateQueryData();
+ method public final String getId();
+ method public final String getType();
+ property public final android.os.Bundle candidateQueryData;
+ property public final String id;
+ property public final String type;
+ }
+
+ public final class BeginGetCredentialRequest {
+ ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
+ ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional android.service.credentials.CallingAppInfo? callingAppInfo);
+ method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
+ method public android.service.credentials.CallingAppInfo? getCallingAppInfo();
+ method public static androidx.credentials.provider.BeginGetCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
+ property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
+ property public final android.service.credentials.CallingAppInfo? callingAppInfo;
+ field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
+ }
+
+ public static final class BeginGetCredentialRequest.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialRequest? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
+ }
+
+ public final class BeginGetCredentialResponse {
+ ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
+ method public java.util.List<androidx.credentials.provider.Action> getActions();
+ method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
+ method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
+ method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
+ method public static androidx.credentials.provider.BeginGetCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public static android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
+ property public final java.util.List<androidx.credentials.provider.Action> actions;
+ property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
+ property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
+ property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
+ field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
+ }
+
+ public static final class BeginGetCredentialResponse.Builder {
+ ctor public BeginGetCredentialResponse.Builder();
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
+ method public androidx.credentials.provider.BeginGetCredentialResponse build();
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
+ method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
+ }
+
+ public static final class BeginGetCredentialResponse.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialResponse? readFromBundle(android.os.Bundle bundle);
+ method public android.os.Bundle writeToBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
+ }
+
+ public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
+ }
+
+ public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
+ method public java.util.Set<java.lang.String> getAllowedUserIds();
+ property public final java.util.Set<java.lang.String> allowedUserIds;
+ }
+
+ public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
+ ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
+ ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
+ method public byte[]? getClientDataHash();
+ method public String getRequestJson();
+ property public final byte[]? clientDataHash;
+ property public final String requestJson;
+ }
+
+ @RequiresApi(28) public final class CreateEntry {
+ ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
+ method public CharSequence getAccountName();
+ method public CharSequence? getDescription();
+ method public android.graphics.drawable.Icon? getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public Integer? getPasswordCredentialCount();
+ method public android.app.PendingIntent getPendingIntent();
+ method public Integer? getPublicKeyCredentialCount();
+ method public Integer? getTotalCredentialCount();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence accountName;
+ property public final CharSequence? description;
+ property public final android.graphics.drawable.Icon? icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ }
+
+ public static final class CreateEntry.Builder {
+ ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
+ method public androidx.credentials.provider.CreateEntry build();
+ method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
+ method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
+ method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
+ method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
+ method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
+ }
+
+ public abstract class CredentialEntry {
+ method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+ property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
+ }
+
+ @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
+ ctor public CredentialProviderService();
+ method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
+ method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
+ method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
+ method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
+ method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
+ method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,androidx.credentials.exceptions.ClearCredentialException> callback);
+ }
+
+ @RequiresApi(28) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence? getSubtitle();
+ method public CharSequence getTitle();
+ method public String getType();
+ method public CharSequence? getTypeDisplayName();
+ method public boolean isAutoSelectAllowed();
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence? subtitle;
+ property public final CharSequence title;
+ property public String type;
+ property public final CharSequence? typeDisplayName;
+ }
+
+ public static final class CustomCredentialEntry.Builder {
+ ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
+ method public androidx.credentials.provider.CustomCredentialEntry build();
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
+ method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
+ }
+
+ @RequiresApi(28) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public CharSequence? getDisplayName();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTypeDisplayName();
+ method public CharSequence getUsername();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence? displayName;
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence typeDisplayName;
+ property public final CharSequence username;
+ }
+
+ public static final class PasswordCredentialEntry.Builder {
+ ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
+ method public androidx.credentials.provider.PasswordCredentialEntry build();
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ }
+
+ @RequiresApi(34) public final class PendingIntentHandler {
+ ctor public PendingIntentHandler();
+ method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
+ method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
+ method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
+ method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
+ method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
+ method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
+ method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
+ method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
+ field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
+ }
+
+ public static final class PendingIntentHandler.Companion {
+ method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
+ method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
+ method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
+ method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
+ method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
+ method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
+ method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
+ method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
+ }
+
+ public final class ProviderClearCredentialStateRequest {
+ ctor public ProviderClearCredentialStateRequest(android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ }
+
+ public final class ProviderCreateCredentialRequest {
+ ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ method public androidx.credentials.CreateCredentialRequest getCallingRequest();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ property public final androidx.credentials.CreateCredentialRequest callingRequest;
+ }
+
+ @RequiresApi(34) public final class ProviderGetCredentialRequest {
+ ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, android.service.credentials.CallingAppInfo callingAppInfo);
+ method public android.service.credentials.CallingAppInfo getCallingAppInfo();
+ method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
+ property public final android.service.credentials.CallingAppInfo callingAppInfo;
+ property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
+ }
+
+ @RequiresApi(28) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+ ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
+ method public CharSequence? getDisplayName();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.time.Instant? getLastUsedTime();
+ method public android.app.PendingIntent getPendingIntent();
+ method public CharSequence getTypeDisplayName();
+ method public CharSequence getUsername();
+ method public boolean isAutoSelectAllowed();
+ property public final CharSequence? displayName;
+ property public final android.graphics.drawable.Icon icon;
+ property public final boolean isAutoSelectAllowed;
+ property public final java.time.Instant? lastUsedTime;
+ property public final android.app.PendingIntent pendingIntent;
+ property public final CharSequence typeDisplayName;
+ property public final CharSequence username;
+ }
+
+ public static final class PublicKeyCredentialEntry.Builder {
+ ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry build();
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
+ method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
+ }
+
+ public final class RemoteEntry {
+ ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
+ method public android.app.PendingIntent getPendingIntent();
+ property public final android.app.PendingIntent pendingIntent;
+ }
+
+}
+
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index c548aa0..0a9bae2 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -26,6 +26,7 @@
api("androidx.annotation:annotation:1.5.0")
api(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesCore)
+ api(project(":core:core"))
androidTestImplementation("androidx.activity:activity:1.2.0")
androidTestImplementation(libs.junit)
@@ -36,6 +37,9 @@
androidTestImplementation(libs.truth)
androidTestImplementation(project(":internal-testutils-truth"))
androidTestImplementation(libs.kotlinCoroutinesAndroid)
+ androidTestImplementation(project(":internal-testutils-runtime"), {
+ exclude group: "androidx.fragment", module: "fragment"
+ })
}
android {
diff --git a/credentials/credentials/lint-baseline.xml b/credentials/credentials/lint-baseline.xml
index f43f80f..8745e71 100644
--- a/credentials/credentials/lint-baseline.xml
+++ b/credentials/credentials/lint-baseline.xml
@@ -13,6 +13,384 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): Action? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): Action? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice(authenticationAction: AuthenticationAction): Slice {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice(authenticationAction: AuthenticationAction): Slice {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): AuthenticationAction? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): AuthenticationAction? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1="class BeginCreateCredentialUtil {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/utils/BeginCreateCredentialUtil.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val id: String,"
+ errorLine2=" ~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val id: String,"
+ errorLine2=" ~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val id: String,"
+ errorLine2=" ~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val type: String,"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val type: String,"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val type: String,"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val candidateQueryData: Bundle"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val candidateQueryData: Bundle"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" open val candidateQueryData: Bundle"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom("
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom("
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, id: String): BeginGetPasswordOption {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, id: String): BeginGetPasswordOption {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice(data: Bundle, id: String): BeginGetPasswordOption {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice(data: Bundle, id: String): BeginGetPasswordOption {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, id: String): BeginGetPublicKeyCredentialOption {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFrom(data: Bundle, id: String): BeginGetPublicKeyCredentialOption {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice(data: Bundle, id: String):"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun createFromEntrySlice(data: Bundle, id: String):"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1=" open val type: String,"
errorLine2=" ~~~~">
<location
@@ -220,168 +598,6 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val credentialData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val credentialData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val credentialData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" val displayInfo: DisplayInfo,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" val displayInfo: DisplayInfo,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" val displayInfo: DisplayInfo,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
errorLine1=" class DisplayInfo internal /** @hide */ constructor("
errorLine2=" ~~~~~~~~~~~">
<location
@@ -418,8 +634,8 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" val defaultProvider: String?,"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ errorLine1=" val preferDefaultProvider: String?,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
</issue>
@@ -427,8 +643,8 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" val defaultProvider: String?,"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ errorLine1=" val preferDefaultProvider: String?,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
</issue>
@@ -436,8 +652,8 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" val defaultProvider: String?,"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ errorLine1=" val preferDefaultProvider: String?,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/credentials/CreateCredentialRequest.kt"/>
</issue>
@@ -544,60 +760,6 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/CreateCredentialResponse.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
@@ -646,6 +808,87 @@
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): CreateEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): CreateEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun convertCredentialCountInfoToBundle("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" internal fun convertCredentialCountInfoToBundle("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
file="src/main/java/androidx/credentials/CreatePasswordRequest.kt"/>
</issue>
@@ -724,60 +967,6 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val type: String,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val data: Bundle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/credentials/Credential.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
@@ -808,7 +997,7 @@
errorLine1=" open val type: String,"
errorLine2=" ~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
@@ -817,7 +1006,7 @@
errorLine1=" open val type: String,"
errorLine2=" ~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
@@ -826,115 +1015,43 @@
errorLine1=" open val type: String,"
errorLine2=" ~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val requestData: Bundle,"
- errorLine2=" ~~~~~~~~~~~">
+ errorLine1=" val slice: Slice"
+ errorLine2=" ~~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val requestData: Bundle,"
- errorLine2=" ~~~~~~~~~~~">
+ errorLine1=" val slice: Slice"
+ errorLine2=" ~~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val requestData: Bundle,"
- errorLine2=" ~~~~~~~~~~~">
+ errorLine1=" val slice: Slice"
+ errorLine2=" ~~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
<location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val candidateQueryData: Bundle,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isSystemProviderRequired: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
- </issue>
-
- <issue
- id="BanHideAnnotation"
- message="@hide is not allowed in Javadoc"
- errorLine1=" open val isAutoSelectAllowed: Boolean,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/credentials/CredentialOption.kt"/>
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
</issue>
<issue
@@ -985,6 +1102,123 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1="class CredentialProviderFrameworkImpl(context: Context) : CredentialProvider {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): CustomCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): CustomCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
@@ -1177,6 +1411,51 @@
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
+ file="src/main/java/androidx/credentials/GetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toRequestDataBundle("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/GetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toRequestDataBundle("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/GetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun createFrom("
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/GetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun createFrom("
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/GetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
file="src/main/java/androidx/credentials/exceptions/GetCredentialUnknownException.kt"/>
</issue>
@@ -1399,19 +1678,55 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" companion object {"
- errorLine2=" ~~~~~~">
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PasswordCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" const val TYPE_PASSWORD_CREDENTIAL: String = "android.credentials.TYPE_PASSWORD_CREDENTIAL""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PasswordCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
</issue>
<issue
@@ -1420,16 +1735,151 @@
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
- file="src/main/java/androidx/credentials/PublicKeyCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
</issue>
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
- errorLine1=" const val TYPE_PUBLIC_KEY_CREDENTIAL: String ="
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PublicKeyCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): PasswordCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): PasswordCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val autoSelectAllowedFromOption: Boolean = false,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" val isDefaultIcon: Boolean = false"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
</issue>
<issue
@@ -1456,6 +1906,51 @@
errorLine1=" companion object {"
errorLine2=" ~~~~~~">
<location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun toSlice("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): RemoteEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" fun fromSlice(slice: Slice): RemoteEntry? {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" companion object {"
+ errorLine2=" ~~~~~~">
+ <location
file="src/main/java/androidx/credentials/exceptions/domerrors/SecurityError.kt"/>
</issue>
@@ -1514,6 +2009,1230 @@
</issue>
<issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Uri.EMPTY, SliceSpec("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Uri.EMPTY, SliceSpec("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_TITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_TITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" title = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" title = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" subtitle = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" subtitle = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Uri.EMPTY, SliceSpec("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Uri.EMPTY, SliceSpec("
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText(title, /*subType=*/null, listOf(SLICE_HINT_TITLE))"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addText(title, /*subType=*/null, listOf(SLICE_HINT_TITLE))"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_TITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_TITLE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" title = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" title = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addLong("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addLong("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 26; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime.toEpochMilli(), /*subType=*/null, listOf("
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 26; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime.toEpochMilli(), /*subType=*/null, listOf("
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addText("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addIcon("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addIcon("
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addBundle("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addBundle("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(),"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" accountName = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" accountName = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_ICON)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_ICON)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" icon = it.icon"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" icon = it.icon"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 26; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime = Instant.ofEpochMilli(it.long)"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 26; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime = Instant.ofEpochMilli(it.long)"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime = Instant.ofEpochMilli(it.long)"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" lastUsedTime = Instant.ofEpochMilli(it.long)"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_NOTE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" } else if (it.hasHint(SLICE_HINT_NOTE)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" description = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CreateEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" description = it.text"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" when (slice.spec?.type) {"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" when (slice.spec?.type) {"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" when (slice.spec?.type) {"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" when (slice.spec?.type) {"
+ errorLine2=" ~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" sliceBuilder.addAction("
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" Slice.Builder(sliceBuilder)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(), /*subType=*/null"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" .build(), /*subType=*/null"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" return sliceBuilder.build()"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" slice.items.forEach {"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="ClassVerificationFailure"
+ message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
+ errorLine1=" pendingIntent = it.action"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
id="UsesNonDefaultVisibleForTesting"
message="Found non-default `otherwise` value for @VisibleForTesting"
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)"
@@ -1525,6 +3244,51 @@
<issue
id="UsesNonDefaultVisibleForTesting"
message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/Action.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1564,6 +3328,69 @@
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CreateEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
file="src/main/java/androidx/credentials/CreatePasswordRequest.kt"/>
</issue>
@@ -1597,6 +3424,114 @@
<issue
id="UsesNonDefaultVisibleForTesting"
message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1780,7 +3715,7 @@
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PasswordCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
</issue>
<issue
@@ -1789,7 +3724,7 @@
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PasswordCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
</issue>
<issue
@@ -1798,7 +3733,196 @@
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/credentials/PublicKeyCredential.kt"/>
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt"/>
</issue>
<issue
@@ -1822,6 +3946,15 @@
<issue
id="UsesNonDefaultVisibleForTesting"
message="Found non-default `otherwise` value for @VisibleForTesting"
+ errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
+ </issue>
+
+ <issue
+ id="UsesNonDefaultVisibleForTesting"
+ message="Found non-default `otherwise` value for @VisibleForTesting"
errorLine1=" @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
index 40acdb0..62ef10f 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
@@ -78,7 +78,24 @@
assertThat(displayInfo.getUserId()).isEqualTo(expectedUserId);
assertThat(displayInfo.getUserDisplayName()).isEqualTo(expectedDisplayName);
assertThat(displayInfo.getCredentialTypeIcon()).isNull();
- assertThat(displayInfo.getDefaultProvider()).isNull();
+ assertThat(displayInfo.getPreferDefaultProvider()).isNull();
+ }
+
+ @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+ @Test
+ public void constructWithUserIdAndDisplayNameAndDefaultProvider_success() {
+ CharSequence expectedUserId = "userId";
+ CharSequence expectedDisplayName = "displayName";
+ String expectedDefaultProvider = "com.test/com.test.TestProviderComponent";
+
+ CreateCredentialRequest.DisplayInfo displayInfo =
+ new CreateCredentialRequest.DisplayInfo(expectedUserId,
+ expectedDisplayName, expectedDefaultProvider);
+
+ assertThat(displayInfo.getUserId()).isEqualTo(expectedUserId);
+ assertThat(displayInfo.getUserDisplayName()).isEqualTo(expectedDisplayName);
+ assertThat(displayInfo.getCredentialTypeIcon()).isNull();
+ assertThat(displayInfo.getPreferDefaultProvider()).isEqualTo(expectedDefaultProvider);
}
@SdkSuppress(minSdkVersion = 28)
@@ -96,7 +113,7 @@
assertThat(displayInfo.getUserId()).isEqualTo(expectedUserId);
assertThat(displayInfo.getUserDisplayName()).isEqualTo(expectedDisplayName);
assertThat(displayInfo.getCredentialTypeIcon()).isEqualTo(expectedIcon);
- assertThat(displayInfo.getDefaultProvider()).isEqualTo(expectedDefaultProvider);
+ assertThat(displayInfo.getPreferDefaultProvider()).isEqualTo(expectedDefaultProvider);
}
@SdkSuppress(minSdkVersion = 28)
@@ -115,6 +132,6 @@
assertThat(displayInfo.getUserDisplayName()).isNull();
assertThat(displayInfo.getCredentialTypeIcon().getResId()).isEqualTo(
R.drawable.ic_password);
- assertThat(displayInfo.getDefaultProvider()).isNull();
+ assertThat(displayInfo.getPreferDefaultProvider()).isNull();
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
index bfde3e9..a495264 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
@@ -66,7 +66,26 @@
assertThat(displayInfo.userId).isEqualTo(expectedUserId)
assertThat(displayInfo.userDisplayName).isEqualTo(expectedDisplayName)
assertThat(displayInfo.credentialTypeIcon).isNull()
- assertThat(displayInfo.defaultProvider).isNull()
+ assertThat(displayInfo.preferDefaultProvider).isNull()
+ }
+
+ @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+ @Test
+ fun constructWithUserIdAndDisplayNameAndDefaultProvider_success() {
+ val expectedUserId: CharSequence = "userId"
+ val expectedDisplayName: CharSequence = "displayName"
+ val expectedDefaultProvider = "com.test/com.test.TestProviderComponent"
+
+ val displayInfo = DisplayInfo(
+ userId = expectedUserId,
+ userDisplayName = expectedDisplayName,
+ preferDefaultProvider = expectedDefaultProvider
+ )
+
+ assertThat(displayInfo.userId).isEqualTo(expectedUserId)
+ assertThat(displayInfo.userDisplayName).isEqualTo(expectedDisplayName)
+ assertThat(displayInfo.credentialTypeIcon).isNull()
+ assertThat(displayInfo.preferDefaultProvider).isEqualTo(expectedDefaultProvider)
}
@SdkSuppress(minSdkVersion = 28)
@@ -85,7 +104,7 @@
assertThat(displayInfo.userId).isEqualTo(expectedUserId)
assertThat(displayInfo.userDisplayName).isEqualTo(expectedDisplayName)
assertThat(displayInfo.credentialTypeIcon).isEqualTo(expectedIcon)
- assertThat(displayInfo.defaultProvider).isEqualTo(expectedDefaultProvider)
+ assertThat(displayInfo.preferDefaultProvider).isEqualTo(expectedDefaultProvider)
}
@SdkSuppress(minSdkVersion = 28)
@@ -105,6 +124,6 @@
assertThat(displayInfo.credentialTypeIcon?.resId).isEqualTo(
R.drawable.ic_password
)
- assertThat(displayInfo.defaultProvider).isNull()
+ assertThat(displayInfo.preferDefaultProvider).isNull()
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestJavaTest.java
index 893f37b..66ef8ac 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestJavaTest.java
@@ -16,12 +16,17 @@
package androidx.credentials;
+import static androidx.credentials.CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED;
+import static androidx.credentials.CreateCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.os.Bundle;
+import androidx.test.filters.SdkSuppress;
+
import org.junit.Test;
public class CreateCustomCredentialRequestJavaTest {
@@ -71,24 +76,37 @@
new CreateCredentialRequest.DisplayInfo("userId"), true);
}
+ @SdkSuppress(minSdkVersion = 26)
@Test
public void getter() {
String expectedType = "TYPE";
- Bundle expectedCredentialDataBundle = new Bundle();
- expectedCredentialDataBundle.putString("Test", "Test");
- Bundle expectedCandidateQueryDataBundle = new Bundle();
- expectedCandidateQueryDataBundle.putBoolean("key", true);
+ boolean expectedAutoSelectAllowed = true;
+ boolean expectedPreferImmediatelyAvailableCredentials = true;
+ Bundle inputCredentialDataBundle = new Bundle();
+ inputCredentialDataBundle.putString("Test", "Test");
+ Bundle expectedCredentialDataBundle = inputCredentialDataBundle.deepCopy();
+ expectedCredentialDataBundle.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelectAllowed);
+ expectedCredentialDataBundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ expectedPreferImmediatelyAvailableCredentials);
+ Bundle inputCandidateQueryDataBundle = new Bundle();
+ inputCandidateQueryDataBundle.putBoolean("key", true);
+ Bundle expectedCandidateQueryDataBundle = inputCandidateQueryDataBundle.deepCopy();
+ expectedCandidateQueryDataBundle.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelectAllowed);
CreateCredentialRequest.DisplayInfo expectedDisplayInfo =
new CreateCredentialRequest.DisplayInfo("userId");
boolean expectedSystemProvider = true;
- boolean expectedAutoSelectAllowed = false;
+ String expectedOrigin = "Origin";
CreateCustomCredentialRequest request = new CreateCustomCredentialRequest(expectedType,
- expectedCredentialDataBundle,
- expectedCandidateQueryDataBundle,
+ inputCredentialDataBundle,
+ inputCandidateQueryDataBundle,
expectedSystemProvider,
expectedDisplayInfo,
- expectedAutoSelectAllowed);
+ expectedAutoSelectAllowed,
+ expectedOrigin,
+ expectedPreferImmediatelyAvailableCredentials);
assertThat(request.getType()).isEqualTo(expectedType);
assertThat(TestUtilsKt.equals(request.getCredentialData(), expectedCredentialDataBundle))
@@ -97,7 +115,10 @@
expectedCandidateQueryDataBundle)).isTrue();
assertThat(request.isSystemProviderRequired()).isEqualTo(expectedSystemProvider);
assertThat(request.isAutoSelectAllowed()).isEqualTo(expectedAutoSelectAllowed);
+ assertThat(request.preferImmediatelyAvailableCredentials()).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials);
assertThat(request.getDisplayInfo()).isEqualTo(expectedDisplayInfo);
+ assertThat(request.getOrigin()).isEqualTo(expectedOrigin);
}
@Test
@@ -107,4 +128,57 @@
new Bundle(), new Bundle(), false,
/* requestDisplayInfo= */null, false));
}
+
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
+ public void frameworkConversion_success() {
+ String expectedType = "TYPE";
+ Bundle expectedCredentialDataBundle = new Bundle();
+ expectedCredentialDataBundle.putString("Test", "Test");
+ Bundle expectedCandidateQueryDataBundle = new Bundle();
+ expectedCandidateQueryDataBundle.putBoolean("key", true);
+ CreateCredentialRequest.DisplayInfo expectedDisplayInfo =
+ new CreateCredentialRequest.DisplayInfo("userId");
+ boolean expectedSystemProvider = true;
+ boolean expectedAutoSelectAllowed = true;
+ boolean expectedPreferImmediatelyAvailableCredentials = true;
+ String expectedOrigin = "Origin";
+ CreateCustomCredentialRequest request = new CreateCustomCredentialRequest(expectedType,
+ expectedCredentialDataBundle,
+ expectedCandidateQueryDataBundle,
+ expectedSystemProvider,
+ expectedDisplayInfo,
+ expectedAutoSelectAllowed,
+ expectedOrigin,
+ expectedPreferImmediatelyAvailableCredentials);
+ Bundle finalCredentialData = request.getCredentialData();
+ finalCredentialData.putBundle(
+ CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_REQUEST_DISPLAY_INFO,
+ expectedDisplayInfo.toBundle()
+ );
+
+ CreateCredentialRequest convertedRequest = CreateCredentialRequest.createFrom(
+ request.getType(), request.getCredentialData(), request.getCandidateQueryData(),
+ request.isSystemProviderRequired(), request.getOrigin());
+
+ assertThat(convertedRequest).isInstanceOf(CreateCustomCredentialRequest.class);
+ CreateCustomCredentialRequest actualRequest =
+ (CreateCustomCredentialRequest) convertedRequest;
+ assertThat(actualRequest.getType()).isEqualTo(expectedType);
+ assertThat(TestUtilsKt.equals(actualRequest.getCredentialData(),
+ expectedCredentialDataBundle))
+ .isTrue();
+ assertThat(TestUtilsKt.equals(actualRequest.getCandidateQueryData(),
+ expectedCandidateQueryDataBundle)).isTrue();
+ assertThat(actualRequest.isSystemProviderRequired()).isEqualTo(expectedSystemProvider);
+ assertThat(actualRequest.isAutoSelectAllowed()).isEqualTo(expectedAutoSelectAllowed);
+ assertThat(actualRequest.getDisplayInfo().getUserId())
+ .isEqualTo(expectedDisplayInfo.getUserId());
+ assertThat(actualRequest.getDisplayInfo().getUserDisplayName())
+ .isEqualTo(expectedDisplayInfo.getUserDisplayName());
+ assertThat(actualRequest.getOrigin()).isEqualTo(expectedOrigin);
+ assertThat(actualRequest.preferImmediatelyAvailableCredentials()).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials);
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestTest.kt
index def2dfb..ccc00f3 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCustomCredentialRequestTest.kt
@@ -17,7 +17,9 @@
package androidx.credentials
import android.os.Bundle
+import androidx.credentials.CreateCredentialRequest.Companion.createFrom
import androidx.credentials.CreateCredentialRequest.DisplayInfo
+import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
@@ -36,24 +38,43 @@
}
}
+ @SdkSuppress(minSdkVersion = 26)
@Test
fun getter() {
val expectedType = "TYPE"
- val expectedCredentialDataBundle = Bundle()
- expectedCredentialDataBundle.putString("Test", "Test")
- val expectedCandidateQueryDataBundle = Bundle()
- expectedCandidateQueryDataBundle.putBoolean("key", true)
+ val expectedAutoSelectAllowed = true
+ val expectedPreferImmediatelyAvailableCredentials = true
+ val inputCredentialDataBundle = Bundle()
+ inputCredentialDataBundle.putString("Test", "Test")
+ val expectedCredentialDataBundle = inputCredentialDataBundle.deepCopy()
+ expectedCredentialDataBundle.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelectAllowed
+ )
+ expectedCredentialDataBundle.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ expectedPreferImmediatelyAvailableCredentials
+ )
+ val inputCandidateQueryDataBundle = Bundle()
+ inputCandidateQueryDataBundle.putBoolean("key", true)
+ val expectedCandidateQueryDataBundle = inputCandidateQueryDataBundle.deepCopy()
+ expectedCandidateQueryDataBundle.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelectAllowed
+ )
val expectedDisplayInfo = DisplayInfo("userId")
- val expectedAutoSelectAllowed = false
val expectedSystemProvider = true
+ val expectedOrigin = "Origin"
val request = CreateCustomCredentialRequest(
expectedType,
- expectedCredentialDataBundle,
- expectedCandidateQueryDataBundle,
+ inputCredentialDataBundle,
+ inputCandidateQueryDataBundle,
expectedSystemProvider,
expectedDisplayInfo,
- expectedAutoSelectAllowed
+ expectedAutoSelectAllowed,
+ expectedOrigin,
+ expectedPreferImmediatelyAvailableCredentials
)
assertThat(request.type).isEqualTo(expectedType)
@@ -65,8 +86,74 @@
expectedCandidateQueryDataBundle
)
).isTrue()
- assertThat(request.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
assertThat(request.isSystemProviderRequired).isEqualTo(expectedSystemProvider)
+ assertThat(request.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
+ assertThat(request.preferImmediatelyAvailableCredentials).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials
+ )
assertThat(request.displayInfo).isEqualTo(expectedDisplayInfo)
+ assertThat(request.origin).isEqualTo(expectedOrigin)
+ }
+
+ @SdkSuppress(minSdkVersion = 23)
+ @Test
+ fun frameworkConversion_success() {
+ val expectedType = "TYPE"
+ val expectedCredentialDataBundle = Bundle()
+ expectedCredentialDataBundle.putString("Test", "Test")
+ val expectedCandidateQueryDataBundle = Bundle()
+ expectedCandidateQueryDataBundle.putBoolean("key", true)
+ val expectedDisplayInfo = DisplayInfo("userId")
+ val expectedSystemProvider = true
+ val expectedAutoSelectAllowed = true
+ val expectedPreferImmediatelyAvailableCredentials = true
+ val expectedOrigin = "Origin"
+ val request = CreateCustomCredentialRequest(
+ expectedType,
+ expectedCredentialDataBundle,
+ expectedCandidateQueryDataBundle,
+ expectedSystemProvider,
+ expectedDisplayInfo,
+ expectedAutoSelectAllowed,
+ expectedOrigin,
+ expectedPreferImmediatelyAvailableCredentials,
+ )
+ val finalCredentialData = request.credentialData
+ finalCredentialData.putBundle(
+ DisplayInfo.BUNDLE_KEY_REQUEST_DISPLAY_INFO,
+ expectedDisplayInfo.toBundle()
+ )
+
+ val convertedRequest = createFrom(
+ request.type, request.credentialData, request.candidateQueryData,
+ request.isSystemProviderRequired, request.origin
+ )!!
+
+ assertThat(convertedRequest).isInstanceOf(CreateCustomCredentialRequest::class.java)
+ val actualRequest = convertedRequest as CreateCustomCredentialRequest
+ assertThat(actualRequest.type).isEqualTo(expectedType)
+ assertThat(
+ equals(
+ actualRequest.credentialData,
+ expectedCredentialDataBundle
+ )
+ ).isTrue()
+ assertThat(
+ equals(
+ actualRequest.candidateQueryData,
+ expectedCandidateQueryDataBundle
+ )
+ ).isTrue()
+ assertThat(actualRequest.isSystemProviderRequired).isEqualTo(expectedSystemProvider)
+ assertThat(actualRequest.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
+ assertThat(actualRequest.displayInfo.userId)
+ .isEqualTo(expectedDisplayInfo.userId)
+ assertThat(actualRequest.displayInfo.userDisplayName)
+ .isEqualTo(expectedDisplayInfo.userDisplayName)
+ assertThat(actualRequest.origin).isEqualTo(expectedOrigin)
+ assertThat(actualRequest.origin).isEqualTo(expectedOrigin)
+ assertThat(actualRequest.preferImmediatelyAvailableCredentials).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials
+ )
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
index d002fbd..18a4ee9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
@@ -16,6 +16,7 @@
package androidx.credentials;
+import static androidx.credentials.CreateCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS;
import static androidx.credentials.internal.FrameworkImplHelper.getFinalCreateCredentialData;
import static com.google.common.truth.Truth.assertThat;
@@ -56,6 +57,38 @@
}
@Test
+ public void constructor_withDefaults() {
+ String idExpected = "id";
+ String passwordExpected = "password";
+
+ CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected);
+
+ assertThat(request.getDisplayInfo().getPreferDefaultProvider()).isNull();
+ assertThat(request.preferImmediatelyAvailableCredentials()).isFalse();
+ assertThat(request.getOrigin()).isNull();
+ assertThat(request.getId()).isEqualTo(idExpected);
+ assertThat(request.getPassword()).isEqualTo(passwordExpected);
+ }
+
+ @Test
+ public void constructor_withoutDefaults() {
+ String idExpected = "id";
+ String passwordExpected = "password";
+ String originExpected = "origin";
+ boolean preferImmediatelyAvailableCredentialsExpected = true;
+
+ CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected,
+ originExpected, preferImmediatelyAvailableCredentialsExpected);
+
+ assertThat(request.preferImmediatelyAvailableCredentials())
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected);
+ assertThat(request.getDisplayInfo().getPreferDefaultProvider()).isNull();
+ assertThat(request.getOrigin()).isEqualTo(originExpected);
+ assertThat(request.getId()).isEqualTo(idExpected);
+ assertThat(request.getPassword()).isEqualTo(passwordExpected);
+ }
+
+ @Test
public void constructor_emptyPassword_throws() {
assertThrows(
IllegalArgumentException.class,
@@ -63,6 +96,28 @@
);
}
+ @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+ @Test
+ public void constructor_defaultProviderVariant() {
+ String idExpected = "id";
+ String passwordExpected = "pwd";
+ String originExpected = "origin";
+ boolean preferImmediatelyAvailableCredentialsExpected = true;
+ String defaultProviderExpected = "com.test/com.test.TestProviderComponent";
+
+ CreatePasswordRequest request = new CreatePasswordRequest(
+ idExpected, passwordExpected, originExpected, defaultProviderExpected,
+ preferImmediatelyAvailableCredentialsExpected);
+
+ assertThat(request.getDisplayInfo().getPreferDefaultProvider())
+ .isEqualTo(defaultProviderExpected);
+ assertThat(request.preferImmediatelyAvailableCredentials())
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected);
+ assertThat(request.getOrigin()).isEqualTo(originExpected);
+ assertThat(request.getId()).isEqualTo(idExpected);
+ assertThat(request.getPassword()).isEqualTo(passwordExpected);
+ }
+
@Test
public void getter_id() {
String idExpected = "id";
@@ -83,29 +138,39 @@
public void getter_frameworkProperties() {
String idExpected = "id";
String passwordExpected = "pwd";
- Bundle expectedData = new Bundle();
+ boolean preferImmediatelyAvailableCredentialsExpected = true;
+ Bundle expectedCredentialData = new Bundle();
boolean expectedAutoSelect = false;
- expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected);
- expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD, passwordExpected);
- expectedData.putBoolean(CreatePasswordRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedCredentialData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected);
+ expectedCredentialData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD,
+ passwordExpected);
+ expectedCredentialData.putBoolean(CreatePasswordRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelect);
+ expectedCredentialData.putBoolean(
+ BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ preferImmediatelyAvailableCredentialsExpected);
+ Bundle expectedCandidateData = new Bundle();
+ expectedCandidateData.putBoolean(CreatePasswordRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
expectedAutoSelect);
- CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected);
+ CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected,
+ /*origin=*/ null, preferImmediatelyAvailableCredentialsExpected);
assertThat(request.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
CreateCredentialRequest.DisplayInfo displayInfo =
request.getDisplayInfo();
assertThat(displayInfo.getUserDisplayName()).isNull();
assertThat(displayInfo.getUserId()).isEqualTo(idExpected);
- assertThat(TestUtilsKt.equals(request.getCandidateQueryData(), Bundle.EMPTY)).isTrue();
+ assertThat(TestUtilsKt.equals(request.getCandidateQueryData(), expectedCandidateData))
+ .isTrue();
assertThat(request.isSystemProviderRequired()).isFalse();
Bundle credentialData =
getFinalCreateCredentialData(
request, mContext);
assertThat(credentialData.keySet())
- .hasSize(expectedData.size() + /* added request info */ 1);
- for (String key : expectedData.keySet()) {
- assertThat(credentialData.get(key)).isEqualTo(credentialData.get(key));
+ .hasSize(expectedCredentialData.size() + /* added request info */ 1);
+ for (String key : expectedCredentialData.keySet()) {
+ assertThat(credentialData.get(key)).isEqualTo(expectedCredentialData.get(key));
}
Bundle displayInfoBundle =
credentialData.getBundle(
@@ -122,7 +187,13 @@
@Test
public void frameworkConversion_success() {
String idExpected = "id";
- CreatePasswordRequest request = new CreatePasswordRequest(idExpected, "password");
+ String passwordExpected = "pwd";
+ boolean preferImmediatelyAvailableCredentialsExpected = true;
+ String originExpected = "origin";
+ String defaultProviderExpected = "com.test/com.test.TestProviderComponent";
+ CreatePasswordRequest request = new CreatePasswordRequest(
+ idExpected, passwordExpected, originExpected, defaultProviderExpected,
+ preferImmediatelyAvailableCredentialsExpected);
CreateCredentialRequest convertedRequest = CreateCredentialRequest.createFrom(
request.getType(), getFinalCreateCredentialData(
@@ -134,13 +205,17 @@
assertThat(convertedRequest).isInstanceOf(CreatePasswordRequest.class);
CreatePasswordRequest convertedCreatePasswordRequest =
(CreatePasswordRequest) convertedRequest;
- assertThat(convertedCreatePasswordRequest.getPassword()).isEqualTo(request.getPassword());
- assertThat(convertedCreatePasswordRequest.getId()).isEqualTo(request.getId());
+ assertThat(convertedCreatePasswordRequest.getPassword()).isEqualTo(passwordExpected);
+ assertThat(convertedCreatePasswordRequest.getId()).isEqualTo(idExpected);
+ assertThat(convertedCreatePasswordRequest.preferImmediatelyAvailableCredentials())
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected);
+ assertThat(convertedCreatePasswordRequest.getOrigin()).isEqualTo(originExpected);
CreateCredentialRequest.DisplayInfo displayInfo =
convertedCreatePasswordRequest.getDisplayInfo();
assertThat(displayInfo.getUserDisplayName()).isNull();
assertThat(displayInfo.getUserId()).isEqualTo(idExpected);
assertThat(displayInfo.getCredentialTypeIcon().getResId())
.isEqualTo(R.drawable.ic_password);
+ assertThat(displayInfo.getPreferDefaultProvider()).isEqualTo(defaultProviderExpected);
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
index 6aeb65d..a9f76dc 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
@@ -18,7 +18,9 @@
import android.graphics.drawable.Icon
import android.os.Bundle
+import android.os.Parcelable
import androidx.credentials.CreateCredentialRequest.Companion.createFrom
+import androidx.credentials.CreateCredentialRequest.DisplayInfo
import androidx.credentials.internal.FrameworkImplHelper.Companion.getFinalCreateCredentialData
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
@@ -43,6 +45,64 @@
}
@Test
+ fun constructor_withDefaults() {
+ val idExpected = "id"
+ val passwordExpected = "password"
+
+ val request = CreatePasswordRequest(idExpected, passwordExpected)
+
+ assertThat(request.displayInfo.preferDefaultProvider).isNull()
+ assertThat(request.preferImmediatelyAvailableCredentials).isFalse()
+ assertThat(request.origin).isNull()
+ assertThat(request.id).isEqualTo(idExpected)
+ assertThat(request.password).isEqualTo(passwordExpected)
+ }
+
+ @Test
+ fun constructor_withoutDefaults() {
+ val idExpected = "id"
+ val passwordExpected = "password"
+ val originExpected = "origin"
+ val preferImmediatelyAvailableCredentialsExpected = true
+
+ val request = CreatePasswordRequest(
+ idExpected, passwordExpected,
+ originExpected, preferImmediatelyAvailableCredentialsExpected
+ )
+
+ assertThat(request.preferImmediatelyAvailableCredentials)
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
+ assertThat(request.displayInfo.preferDefaultProvider).isNull()
+ assertThat(request.origin).isEqualTo(originExpected)
+ assertThat(request.id).isEqualTo(idExpected)
+ assertThat(request.password).isEqualTo(passwordExpected)
+ }
+
+ @Test
+ fun constructor_defaultProviderVariant() {
+ val idExpected = "id"
+ val passwordExpected = "pwd"
+ val originExpected = "origin"
+ val defaultProviderExpected = "com.test/com.test.TestProviderComponent"
+ val preferImmediatelyAvailableCredentialsExpected = true
+
+ val request = CreatePasswordRequest(
+ id = idExpected,
+ password = passwordExpected,
+ origin = originExpected,
+ preferDefaultProvider = defaultProviderExpected,
+ preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentialsExpected,
+ )
+
+ assertThat(request.displayInfo.preferDefaultProvider).isEqualTo(defaultProviderExpected)
+ assertThat(request.origin).isEqualTo(originExpected)
+ assertThat(request.password).isEqualTo(passwordExpected)
+ assertThat(request.id).isEqualTo(idExpected)
+ assertThat(request.preferImmediatelyAvailableCredentials)
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
+ }
+
+ @Test
fun getter_id() {
val idExpected = "id"
val request = CreatePasswordRequest(idExpected, "password")
@@ -62,6 +122,7 @@
fun getter_frameworkProperties() {
val idExpected = "id"
val passwordExpected = "pwd"
+ val preferImmediatelyAvailableCredentialsExpected = true
val expectedCredentialData = Bundle()
val expectedAutoSelect = false
expectedCredentialData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected)
@@ -69,32 +130,53 @@
CreatePasswordRequest.BUNDLE_KEY_PASSWORD,
passwordExpected
)
- expectedCredentialData.putBoolean(CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
- expectedAutoSelect)
+ expectedCredentialData.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelect
+ )
+ expectedCredentialData.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ preferImmediatelyAvailableCredentialsExpected
+ )
+ val expectedCandidateData = Bundle()
+ expectedCandidateData.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ expectedAutoSelect
+ )
- val request = CreatePasswordRequest(idExpected, passwordExpected)
+ val request = CreatePasswordRequest(
+ idExpected, passwordExpected, /*origin=*/null,
+ preferImmediatelyAvailableCredentialsExpected
+ )
assertThat(request.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
- assertThat(equals(request.candidateQueryData, Bundle.EMPTY)).isTrue()
+ val displayInfo = request.displayInfo
+ assertThat(displayInfo.userDisplayName).isNull()
+ assertThat(displayInfo.userId).isEqualTo(idExpected)
+ assertThat(equals(request.candidateQueryData, expectedCandidateData))
+ .isTrue()
assertThat(request.isSystemProviderRequired).isFalse()
- assertThat(request.displayInfo.userDisplayName).isNull()
- assertThat(request.displayInfo.userId).isEqualTo(idExpected)
val credentialData = getFinalCreateCredentialData(
request, mContext
)
assertThat(credentialData.keySet())
- .hasSize(expectedCredentialData.size() + /* added request info */ 1)
+ .hasSize(expectedCredentialData.size() + /* added request info */1)
for (key in expectedCredentialData.keySet()) {
- assertThat(expectedCredentialData.get(key)).isEqualTo(credentialData.get(key))
+ assertThat(credentialData[key]).isEqualTo(expectedCredentialData[key])
}
- val displayInfoBundle =
- credentialData.getBundle(
- CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_REQUEST_DISPLAY_INFO)!!
- assertThat(displayInfoBundle.keySet()).hasSize(2)
- assertThat(displayInfoBundle.getString(
- CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_USER_ID)).isEqualTo(idExpected)
- assertThat((displayInfoBundle.getParcelable(
- CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_CREDENTIAL_TYPE_ICON) as Icon?)!!.resId
+ val displayInfoBundle = credentialData.getBundle(
+ DisplayInfo.BUNDLE_KEY_REQUEST_DISPLAY_INFO
+ )
+ assertThat(displayInfoBundle!!.keySet()).hasSize(2)
+ assertThat(
+ displayInfoBundle.getString(
+ DisplayInfo.BUNDLE_KEY_USER_ID
+ )
+ ).isEqualTo(idExpected)
+ assertThat(
+ (displayInfoBundle.getParcelable<Parcelable>(
+ DisplayInfo.BUNDLE_KEY_CREDENTIAL_TYPE_ICON
+ ) as Icon?)!!.resId
).isEqualTo(R.drawable.ic_password)
}
@@ -102,27 +184,37 @@
@Test
fun frameworkConversion_success() {
val idExpected = "id"
- val request = CreatePasswordRequest(idExpected, "password")
- val origin = "origin"
+ val passwordExpected = "pwd"
+ val preferImmediatelyAvailableCredentialsExpected = true
+ val originExpected = "origin"
+ val defaultProviderExpected = "com.test/com.test.TestProviderComponent"
+ val request = CreatePasswordRequest(
+ idExpected, passwordExpected, originExpected, defaultProviderExpected,
+ preferImmediatelyAvailableCredentialsExpected
+ )
val convertedRequest = createFrom(
request.type, getFinalCreateCredentialData(
request, mContext
),
request.candidateQueryData, request.isSystemProviderRequired,
- origin
+ request.origin
)
assertThat(convertedRequest).isInstanceOf(
CreatePasswordRequest::class.java
)
val convertedCreatePasswordRequest = convertedRequest as CreatePasswordRequest
- assertThat(convertedCreatePasswordRequest.password).isEqualTo(request.password)
- assertThat(convertedCreatePasswordRequest.id).isEqualTo(request.id)
- assertThat(convertedCreatePasswordRequest.displayInfo.userDisplayName).isNull()
- assertThat(convertedCreatePasswordRequest.displayInfo.userId).isEqualTo(idExpected)
- assertThat(convertedCreatePasswordRequest.displayInfo.credentialTypeIcon?.resId)
+ assertThat(convertedCreatePasswordRequest.password).isEqualTo(passwordExpected)
+ assertThat(convertedCreatePasswordRequest.id).isEqualTo(idExpected)
+ assertThat(convertedCreatePasswordRequest.preferImmediatelyAvailableCredentials)
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
+ assertThat(convertedCreatePasswordRequest.origin).isEqualTo(originExpected)
+ val displayInfo = convertedCreatePasswordRequest.displayInfo
+ assertThat(displayInfo.userDisplayName).isNull()
+ assertThat(displayInfo.userId).isEqualTo(idExpected)
+ assertThat(displayInfo.credentialTypeIcon!!.resId)
.isEqualTo(R.drawable.ic_password)
- assertThat(convertedCreatePasswordRequest.origin).isEqualTo(origin)
+ assertThat(displayInfo.preferDefaultProvider).isEqualTo(defaultProviderExpected)
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
index d3af566..c4dba09 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
@@ -16,8 +16,8 @@
package androidx.credentials;
+import static androidx.credentials.CreateCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS;
import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED;
-import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS;
import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_REQUEST_JSON;
import static androidx.credentials.internal.FrameworkImplHelper.getFinalCreateCredentialData;
@@ -82,6 +82,27 @@
}
@Test
+ public void constructor_defaultProviderVariant() {
+ byte[] clientDataHashExpected = "hash".getBytes();
+ String originExpected = "origin";
+ Boolean preferImmediatelyAvailableCredentialsExpected = true;
+ String defaultProviderExpected = "com.test/com.test.TestProviderComponent";
+
+ CreatePublicKeyCredentialRequest request = new CreatePublicKeyCredentialRequest(
+ TEST_REQUEST_JSON, clientDataHashExpected,
+ preferImmediatelyAvailableCredentialsExpected, originExpected,
+ defaultProviderExpected);
+
+ assertThat(request.getDisplayInfo().getPreferDefaultProvider())
+ .isEqualTo(defaultProviderExpected);
+ assertThat(request.getClientDataHash()).isEqualTo(clientDataHashExpected);
+ assertThat(request.getOrigin()).isEqualTo(originExpected);
+ assertThat(request.getRequestJson()).isEqualTo(TEST_REQUEST_JSON);
+ assertThat(request.preferImmediatelyAvailableCredentials())
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected);
+ }
+
+ @Test
public void constructor_setsPreferImmediatelyAvailableCredentialsToFalseByDefault() {
CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
new CreatePublicKeyCredentialRequest(TEST_REQUEST_JSON);
@@ -93,7 +114,7 @@
@Test
public void constructor_setPreferImmediatelyAvailableCredentialsToTrue() {
boolean preferImmediatelyAvailableCredentialsExpected = true;
- String clientDataHash = "hash";
+ byte[] clientDataHash = "hash".getBytes();
CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
new CreatePublicKeyCredentialRequest(TEST_REQUEST_JSON,
clientDataHash,
@@ -119,38 +140,40 @@
@Test
public void getter_frameworkProperties_success() {
String requestJsonExpected = TEST_REQUEST_JSON;
- String clientDataHash = "hash";
- boolean preferImmediatelyAvailableCredentialsExpected = false;
- Bundle expectedData = new Bundle();
- expectedData.putString(
+ byte[] clientDataHash = "hash".getBytes();
+ boolean preferImmediatelyAvailableCredentialsExpected = true;
+ boolean autoSelectExpected = false;
+ Bundle expectedCandidateQueryData = new Bundle();
+ expectedCandidateQueryData.putString(
PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
CreatePublicKeyCredentialRequest
.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST);
- expectedData.putString(
+ expectedCandidateQueryData.putString(
BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
- expectedData.putString(CreatePublicKeyCredentialRequest.BUNDLE_KEY_CLIENT_DATA_HASH,
+ expectedCandidateQueryData.putByteArray(
+ CreatePublicKeyCredentialRequest.BUNDLE_KEY_CLIENT_DATA_HASH,
clientDataHash);
- expectedData.putBoolean(
+ expectedCandidateQueryData.putBoolean(
+ BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ autoSelectExpected);
+ Bundle expectedCredentialData = expectedCandidateQueryData.deepCopy();
+ expectedCredentialData.putBoolean(
BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
preferImmediatelyAvailableCredentialsExpected);
- expectedData.putBoolean(
- BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
- preferImmediatelyAvailableCredentialsExpected);
- Bundle expectedQuery = TestUtilsKt.deepCopyBundle(expectedData);
- expectedQuery.remove(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED);
CreatePublicKeyCredentialRequest request = new CreatePublicKeyCredentialRequest(
requestJsonExpected, clientDataHash, preferImmediatelyAvailableCredentialsExpected);
assertThat(request.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
- assertThat(TestUtilsKt.equals(request.getCandidateQueryData(), expectedQuery)).isTrue();
+ assertThat(TestUtilsKt.equals(request.getCandidateQueryData(), expectedCandidateQueryData))
+ .isTrue();
assertThat(request.isSystemProviderRequired()).isFalse();
Bundle credentialData = getFinalCreateCredentialData(
request, mContext);
assertThat(credentialData.keySet())
- .hasSize(expectedData.size() + /* added request info */ 1);
- for (String key : expectedData.keySet()) {
- assertThat(credentialData.get(key)).isEqualTo(credentialData.get(key));
+ .hasSize(expectedCredentialData.size() + /* added request info */ 1);
+ for (String key : expectedCredentialData.keySet()) {
+ assertThat(credentialData.get(key)).isEqualTo(expectedCredentialData.get(key));
}
Bundle displayInfoBundle =
credentialData.getBundle(
@@ -169,9 +192,12 @@
@SdkSuppress(minSdkVersion = 28)
@Test
public void frameworkConversion_success() {
- String clientDataHash = "hash";
- CreatePublicKeyCredentialRequest request =
- new CreatePublicKeyCredentialRequest(TEST_REQUEST_JSON, clientDataHash, true);
+ byte[] clientDataHashExpected = "hash".getBytes();
+ String originExpected = "origin";
+ Boolean preferImmediatelyAvailableCredentialsExpected = true;
+ CreatePublicKeyCredentialRequest request = new CreatePublicKeyCredentialRequest(
+ TEST_REQUEST_JSON, clientDataHashExpected,
+ preferImmediatelyAvailableCredentialsExpected, originExpected);
CreateCredentialRequest convertedRequest = CreateCredentialRequest.createFrom(
request.getType(), getFinalCreateCredentialData(
@@ -184,8 +210,10 @@
CreatePublicKeyCredentialRequest convertedSubclassRequest =
(CreatePublicKeyCredentialRequest) convertedRequest;
assertThat(convertedSubclassRequest.getRequestJson()).isEqualTo(request.getRequestJson());
+ assertThat(convertedSubclassRequest.getOrigin()).isEqualTo(originExpected);
+ assertThat(convertedSubclassRequest.getClientDataHash()).isEqualTo(clientDataHashExpected);
assertThat(convertedSubclassRequest.preferImmediatelyAvailableCredentials())
- .isEqualTo(request.preferImmediatelyAvailableCredentials());
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected);
CreateCredentialRequest.DisplayInfo displayInfo =
convertedRequest.getDisplayInfo();
assertThat(displayInfo.getUserDisplayName()).isEqualTo(TEST_USER_DISPLAYNAME);
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
index 538da6a..1f5a3df 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
@@ -19,8 +19,8 @@
import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.Parcelable
+import androidx.credentials.CreateCredentialRequest.Companion.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS
import androidx.credentials.CreateCredentialRequest.Companion.createFrom
-import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS
import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_REQUEST_JSON
import androidx.credentials.internal.FrameworkImplHelper.Companion.getFinalCreateCredentialData
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -74,13 +74,15 @@
fun constructor_setPreferImmediatelyAvailableCredentialsToTrue() {
val preferImmediatelyAvailableCredentialsExpected = true
val origin = "origin"
- val clientDataHash = "hash"
+ val clientDataHash = "hash".toByteArray()
+
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
TEST_REQUEST_JSON,
clientDataHash,
preferImmediatelyAvailableCredentialsExpected,
origin
)
+
val preferImmediatelyAvailableCredentialsActual =
createPublicKeyCredentialRequest.preferImmediatelyAvailableCredentials
assertThat(preferImmediatelyAvailableCredentialsActual)
@@ -89,6 +91,28 @@
}
@Test
+ fun constructor_defaultProviderVariant() {
+ val clientDataHashExpected = "hash".toByteArray()
+ val originExpected = "origin"
+ val preferImmediatelyAvailableCredentialsExpected = true
+ val defaultProviderExpected = "com.test/com.test.TestProviderComponent"
+
+ val request = CreatePublicKeyCredentialRequest(
+ TEST_REQUEST_JSON, clientDataHashExpected,
+ preferImmediatelyAvailableCredentialsExpected, originExpected,
+ defaultProviderExpected
+ )
+
+ assertThat(request.displayInfo.preferDefaultProvider)
+ .isEqualTo(defaultProviderExpected)
+ assertThat(request.clientDataHash).isEqualTo(clientDataHashExpected)
+ assertThat(request.origin).isEqualTo(originExpected)
+ assertThat(request.requestJson).isEqualTo(TEST_REQUEST_JSON)
+ assertThat(request.preferImmediatelyAvailableCredentials)
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
+ }
+
+ @Test
fun getter_requestJson_success() {
val testJsonExpected = "{\"user\":{\"name\":{\"lol\":\"Value\"}}}"
val createPublicKeyCredentialReq = CreatePublicKeyCredentialRequest(testJsonExpected)
@@ -101,58 +125,52 @@
@Test
fun getter_frameworkProperties_success() {
val requestJsonExpected = TEST_REQUEST_JSON
- val preferImmediatelyAvailableCredentialsExpected = false
- val expectedAutoSelect = true
- val origin = "origin"
- val clientDataHash = "hash"
- val expectedData = Bundle()
- expectedData.putString(
+ val clientDataHash = "hash".toByteArray()
+ val preferImmediatelyAvailableCredentialsExpected = true
+ val autoSelectExpected = false
+ val expectedCandidateQueryData = Bundle()
+ expectedCandidateQueryData.putString(
PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
CreatePublicKeyCredentialRequest
.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST
)
- expectedData.putString(
+ expectedCandidateQueryData.putString(
BUNDLE_KEY_REQUEST_JSON, requestJsonExpected
)
- expectedData.putString(CreatePublicKeyCredentialRequest.BUNDLE_KEY_CLIENT_DATA_HASH,
- clientDataHash)
- expectedData.putBoolean(
+ expectedCandidateQueryData.putByteArray(
+ CreatePublicKeyCredentialRequest.BUNDLE_KEY_CLIENT_DATA_HASH,
+ clientDataHash
+ )
+ expectedCandidateQueryData.putBoolean(
+ CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
+ autoSelectExpected
+ )
+ val expectedCredentialData = expectedCandidateQueryData.deepCopy()
+ expectedCredentialData.putBoolean(
BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
preferImmediatelyAvailableCredentialsExpected
)
- expectedData.putBoolean(
- CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
- expectedAutoSelect
- )
-
- val expectedCandidateQueryBundle = expectedData.deepCopy()
- expectedCandidateQueryBundle.remove(
- CreateCredentialRequest.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED
- )
val request = CreatePublicKeyCredentialRequest(
- requestJsonExpected,
- clientDataHash,
- preferImmediatelyAvailableCredentialsExpected,
- origin
+ requestJsonExpected, clientDataHash, preferImmediatelyAvailableCredentialsExpected
)
assertThat(request.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
- assertThat(equals(request.candidateQueryData, expectedCandidateQueryBundle)).isTrue()
+ assertThat(equals(request.candidateQueryData, expectedCandidateQueryData))
+ .isTrue()
assertThat(request.isSystemProviderRequired).isFalse()
- assertThat(request.origin).isEqualTo(origin)
val credentialData = getFinalCreateCredentialData(
request, mContext
)
assertThat(credentialData.keySet())
- .hasSize(expectedData.size() + /* added request info */1)
- for (key in expectedData.keySet()) {
- assertThat(credentialData[key]).isEqualTo(credentialData[key])
+ .hasSize(expectedCredentialData.size() + /* added request info */1)
+ for (key in expectedCredentialData.keySet()) {
+ assertThat(credentialData[key]).isEqualTo(expectedCredentialData[key])
}
val displayInfoBundle = credentialData.getBundle(
CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_REQUEST_DISPLAY_INFO
- )!!
- assertThat(displayInfoBundle.keySet()).hasSize(3)
+ )
+ assertThat(displayInfoBundle!!.keySet()).hasSize(3)
assertThat(
displayInfoBundle.getString(
CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_USER_ID
@@ -173,10 +191,13 @@
@SdkSuppress(minSdkVersion = 28)
@Test
fun frameworkConversion_success() {
- val origin = "origin"
- val clientDataHash = "hash"
- val request = CreatePublicKeyCredentialRequest(TEST_REQUEST_JSON, clientDataHash,
- true, origin)
+ val clientDataHashExpected = "hash".toByteArray()
+ val originExpected = "origin"
+ val preferImmediatelyAvailableCredentialsExpected = true
+ val request = CreatePublicKeyCredentialRequest(
+ TEST_REQUEST_JSON, clientDataHashExpected,
+ preferImmediatelyAvailableCredentialsExpected, originExpected
+ )
val convertedRequest = createFrom(
request.type, getFinalCreateCredentialData(
@@ -189,15 +210,16 @@
assertThat(convertedRequest).isInstanceOf(
CreatePublicKeyCredentialRequest::class.java
)
- assertThat(convertedRequest?.origin).isEqualTo(origin)
val convertedSubclassRequest = convertedRequest as CreatePublicKeyCredentialRequest
assertThat(convertedSubclassRequest.requestJson).isEqualTo(request.requestJson)
+ assertThat(convertedSubclassRequest.origin).isEqualTo(originExpected)
+ assertThat(convertedSubclassRequest.clientDataHash).isEqualTo(clientDataHashExpected)
assertThat(convertedSubclassRequest.preferImmediatelyAvailableCredentials)
- .isEqualTo(request.preferImmediatelyAvailableCredentials)
- val displayInfo = convertedRequest.displayInfo
+ .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
+ val displayInfo = convertedSubclassRequest.displayInfo
assertThat(displayInfo.userDisplayName).isEqualTo(TEST_USER_DISPLAYNAME)
assertThat(displayInfo.userId).isEqualTo(TEST_USERNAME)
- assertThat(displayInfo.credentialTypeIcon?.resId)
+ assertThat(displayInfo.credentialTypeIcon!!.resId)
.isEqualTo(R.drawable.ic_passkey)
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
index 85246a1..1a8f8a0 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
@@ -25,14 +25,18 @@
import android.os.Looper;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.credentials.exceptions.ClearCredentialException;
import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException;
import androidx.credentials.exceptions.CreateCredentialException;
+import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException;
import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException;
import androidx.credentials.exceptions.GetCredentialException;
import androidx.credentials.exceptions.GetCredentialProviderConfigurationException;
+import androidx.credentials.exceptions.NoCredentialException;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -46,6 +50,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@RequiresApi(16)
+@SdkSuppress(minSdkVersion = 16)
public class CredentialManagerJavaTest {
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
@@ -68,8 +74,8 @@
ActivityScenario.launch(TestActivity.class);
activityScenario.onActivity(activity -> {
mCredentialManager.createCredentialAsync(
- new CreatePasswordRequest("test-user-id", "test-password"),
activity,
+ new CreatePasswordRequest("test-user-id", "test-password"),
null,
Runnable::run,
new CredentialManagerCallback<CreateCredentialResponse,
@@ -81,20 +87,26 @@
}
@Override
- public void onResult(@NonNull CreateCredentialResponse result) {}
+ public void onResult(@NonNull CreateCredentialResponse result) {
+ }
});
});
latch.await(100L, TimeUnit.MILLISECONDS);
- assertThat(loadedResult.get().getClass()).isEqualTo(
- CreateCredentialProviderConfigurationException.class);
+ if (!isPostFrameworkApiLevel()) {
+ assertThat(loadedResult.get().getClass()).isEqualTo(
+ CreateCredentialProviderConfigurationException.class);
+ } else {
+ assertThat(loadedResult.get().getClass()).isEqualTo(
+ CreateCredentialNoCreateOptionException.class);
+ }
// TODO("Add manifest tests and possibly further separate these tests by API Level
// - maybe a rule perhaps?")
}
-
@Test
- public void testGetCredentialAsyc_successCallbackThrows() throws InterruptedException {
+ public void testGetCredentialAsyc_requestBasedApi_successCallbackThrows()
+ throws InterruptedException {
if (Looper.myLooper() == null) {
Looper.prepare();
}
@@ -102,32 +114,96 @@
AtomicReference<GetCredentialException> loadedResult = new AtomicReference<>();
mCredentialManager.getCredentialAsync(
+ new Activity(),
new GetCredentialRequest.Builder()
.addCredentialOption(new GetPasswordOption())
.build(),
- new Activity(),
null,
Runnable::run,
new CredentialManagerCallback<GetCredentialResponse,
- GetCredentialException>() {
- @Override
- public void onError(@NonNull GetCredentialException e) {
- loadedResult.set(e);
- latch.countDown();
- }
+ GetCredentialException>() {
+ @Override
+ public void onError(@NonNull GetCredentialException e) {
+ loadedResult.set(e);
+ latch.countDown();
+ }
- @Override
- public void onResult(@NonNull GetCredentialResponse result) {}
- });
+ @Override
+ public void onResult(@NonNull GetCredentialResponse result) {
+ }
+ });
latch.await(100L, TimeUnit.MILLISECONDS);
- assertThat(loadedResult.get().getClass()).isEqualTo(
- GetCredentialProviderConfigurationException.class);
+ if (!isPostFrameworkApiLevel()) {
+ assertThat(loadedResult.get().getClass()).isEqualTo(
+ GetCredentialProviderConfigurationException.class);
+ } else {
+ assertThat(loadedResult.get().getClass()).isEqualTo(
+ NoCredentialException.class);
+ }
// TODO("Add manifest tests and possibly further separate these tests - maybe a rule
// perhaps?")
}
@Test
+ @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+ public void testPrepareGetCredentialAsyc_throwsUnimplementedError() throws Exception {
+ CountDownLatch latch1 = new CountDownLatch(1);
+ AtomicReference<PrepareGetCredentialResponse> prepareResult = new AtomicReference<>();
+
+ mCredentialManager.prepareGetCredentialAsync(
+ new GetCredentialRequest.Builder()
+ .addCredentialOption(new GetPasswordOption())
+ .build(),
+ null,
+ Runnable::run,
+ new CredentialManagerCallback<PrepareGetCredentialResponse,
+ GetCredentialException>() {
+ @Override
+ public void onError(@NonNull GetCredentialException e) {}
+
+ @Override
+ public void onResult(@NonNull PrepareGetCredentialResponse result) {
+ prepareResult.set(result);
+ latch1.countDown();
+ }
+ });
+ latch1.await(100L, TimeUnit.MILLISECONDS);
+ assertThat(prepareResult.get()).isNotNull();
+
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ CountDownLatch latch2 = new CountDownLatch(1);
+ AtomicReference<GetCredentialException> getResult = new AtomicReference<>();
+
+ ActivityScenario<TestActivity> activityScenario =
+ ActivityScenario.launch(TestActivity.class);
+ activityScenario.onActivity(activity -> {
+ mCredentialManager.getCredentialAsync(
+ activity,
+ prepareResult.get().getPendingGetCredentialHandle(),
+ null,
+ Runnable::run,
+ new CredentialManagerCallback<GetCredentialResponse,
+ GetCredentialException>() {
+ @Override
+ public void onError(@NonNull GetCredentialException e) {
+ getResult.set(e);
+ latch2.countDown();
+ }
+
+ @Override
+ public void onResult(@NonNull GetCredentialResponse result) {}
+ });
+ });
+
+ latch2.await(100L, TimeUnit.MILLISECONDS);
+ assertThat(getResult.get().getClass()).isEqualTo(NoCredentialException.class);
+ }
+
+ @Test
public void testClearCredentialSessionAsync_throws() throws InterruptedException {
if (isPostFrameworkApiLevel()) {
return; // TODO(Support!)
@@ -148,7 +224,8 @@
}
@Override
- public void onResult(@NonNull Void result) {}
+ public void onResult(@NonNull Void result) {
+ }
});
latch.await(100L, TimeUnit.MILLISECONDS);
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
index 162d6df..e841b77 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
@@ -18,17 +18,22 @@
import android.app.Activity
import android.os.Looper
+import androidx.annotation.RequiresApi
import androidx.credentials.exceptions.ClearCredentialException
import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException
import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
-import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
+import androidx.credentials.exceptions.NoCredentialException
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.testutils.assertThrows
+import androidx.testutils.withActivity
+import androidx.testutils.withUse
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
@@ -41,6 +46,8 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
+@RequiresApi(16)
+@SdkSuppress(minSdkVersion = 16)
class CredentialManagerTest {
private val context = InstrumentationRegistry.getInstrumentation().context
@@ -59,8 +66,8 @@
if (!isPostFrameworkApiLevel()) {
assertThrows<CreateCredentialProviderConfigurationException> {
credentialManager.createCredential(
- CreatePasswordRequest("test-user-id", "test-password"),
- Activity()
+ Activity(),
+ CreatePasswordRequest("test-user-id", "test-password")
)
}
}
@@ -69,7 +76,7 @@
}
@Test
- fun getCredential_throws() = runBlocking<Unit> {
+ fun getCredential_requestBasedApi_throws() = runBlocking<Unit> {
if (Looper.myLooper() == null) {
Looper.prepare()
}
@@ -79,7 +86,11 @@
if (!isPostFrameworkApiLevel()) {
assertThrows<GetCredentialProviderConfigurationException> {
- credentialManager.getCredential(request, Activity())
+ credentialManager.getCredential(Activity(), request)
+ }
+ } else {
+ assertThrows<NoCredentialException> {
+ credentialManager.getCredential(Activity(), request)
}
}
// TODO("Add manifest tests and possibly further separate these tests by API Level
@@ -87,6 +98,30 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+ fun testPrepareGetCredential_throwsUnimplementedError() = runBlocking<Unit> {
+ val prepareGetCredentialResponse = credentialManager.prepareGetCredential(
+ GetCredentialRequest(listOf(GetPasswordOption())))
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare()
+ }
+
+ withUse(ActivityScenario.launch(TestActivity::class.java)) {
+ withActivity {
+ runBlocking {
+ assertThrows<NoCredentialException> {
+ credentialManager.getCredential(
+ this@withActivity,
+ prepareGetCredentialResponse.pendingGetCredentialHandle
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @Test
fun testClearCredentialSession_throws() = runBlocking<Unit> {
if (Looper.myLooper() == null) {
Looper.prepare()
@@ -114,8 +149,8 @@
activityScenario.onActivity { activity ->
credentialManager.createCredentialAsync(
- CreatePasswordRequest("test-user-id", "test-password"),
activity,
+ CreatePasswordRequest("test-user-id", "test-password"),
null, Executor { obj: Runnable -> obj.run() },
object : CredentialManagerCallback<CreateCredentialResponse,
CreateCredentialException> {
@@ -132,47 +167,16 @@
assertThat(loadedResult.get().javaClass).isEqualTo(
CreateCredentialProviderConfigurationException::class.java
)
+ } else {
+ assertThat(loadedResult.get().javaClass).isEqualTo(
+ CreateCredentialNoCreateOptionException::class.java
+ )
}
// TODO("Add manifest tests and possibly further separate these tests by API Level
// - maybe a rule perhaps?")
}
@Test
- fun testGetCredentialAsyc_successCallbackThrows() {
- if (Looper.myLooper() == null) {
- Looper.prepare()
- }
- val latch = CountDownLatch(1)
- val loadedResult: AtomicReference<GetCredentialException> = AtomicReference()
-
- credentialManager.getCredentialAsync(
- request = GetCredentialRequest.Builder()
- .addCredentialOption(GetPasswordOption())
- .build(),
- activity = Activity(),
- cancellationSignal = null,
- executor = Runnable::run,
- callback = object : CredentialManagerCallback<GetCredentialResponse,
- GetCredentialException> {
- override fun onResult(result: GetCredentialResponse) {}
- override fun onError(e: GetCredentialException) {
- loadedResult.set(e)
- latch.countDown()
- }
- }
- )
-
- latch.await(100L, TimeUnit.MILLISECONDS)
- if (!isPostFrameworkApiLevel()) {
- assertThat(loadedResult.get().javaClass).isEqualTo(
- GetCredentialProviderConfigurationException::class.java
- )
- }
- // TODO("Add manifest tests and possibly further separate these tests - maybe a rule
- // perhaps?")
- }
-
- @Test
fun testClearCredentialSessionAsync_throws() {
if (Looper.myLooper() == null) {
Looper.prepare()
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestJavaTest.java
index e8bea47..887aabb 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestJavaTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertThrows;
+import android.content.ComponentName;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -51,6 +53,38 @@
assertThat(request.getCredentialOptions().get(i)).isEqualTo(
expectedCredentialOptions.get(i));
}
+ assertThat(request.getPreferIdentityDocUi()).isFalse();
+ assertThat(request.preferImmediatelyAvailableCredentials()).isFalse();
+ assertThat(request.getPreferUiBrandingComponentName()).isNull();
+ }
+
+ @Test
+ public void constructor_nonDefaultPreferUiBrandingComponentName() {
+ ArrayList<CredentialOption> options = new ArrayList<>();
+ options.add(new GetPasswordOption());
+ ComponentName expectedComponentName = new ComponentName("test pkg", "test cls");
+
+ GetCredentialRequest request = new GetCredentialRequest(
+ options, /*origin=*/ null, /*preferIdentityDocUi=*/false, expectedComponentName);
+
+ assertThat(request.getCredentialOptions().get(0).isAutoSelectAllowed()).isFalse();
+ assertThat(request.getPreferUiBrandingComponentName()).isEqualTo(expectedComponentName);
+ }
+
+ @Test
+ public void constructor_nonDefaultPreferImmediatelyAvailableCredentials() {
+ ArrayList<CredentialOption> options = new ArrayList<>();
+ options.add(new GetPasswordOption());
+ boolean expectedPreferImmediatelyAvailableCredentials = true;
+
+ GetCredentialRequest request = new GetCredentialRequest(
+ options, /*origin=*/ null, /*preferIdentityDocUi=*/false,
+ /*preferUiBrandingComponentName=*/ null,
+ expectedPreferImmediatelyAvailableCredentials);
+
+ assertThat(request.getCredentialOptions().get(0).isAutoSelectAllowed()).isFalse();
+ assertThat(request.preferImmediatelyAvailableCredentials())
+ .isEqualTo(expectedPreferImmediatelyAvailableCredentials);
}
@Test
@@ -61,6 +95,7 @@
GetCredentialRequest request = new GetCredentialRequest(options);
assertThat(request.getCredentialOptions().get(0).isAutoSelectAllowed()).isFalse();
+ assertThat(request.getPreferIdentityDocUi()).isFalse();
}
@Test
@@ -96,6 +131,70 @@
assertThat(request.getCredentialOptions().get(i)).isEqualTo(
expectedCredentialOptions.get(i));
}
+ assertThat(request.getPreferIdentityDocUi()).isFalse();
+ assertThat(request.preferImmediatelyAvailableCredentials()).isFalse();
+ assertThat(request.getPreferUiBrandingComponentName()).isNull();
+ }
+
+ @Test
+ public void builder_setPreferIdentityDocUi() {
+ ArrayList<CredentialOption> expectedCredentialOptions = new ArrayList<>();
+ expectedCredentialOptions.add(new GetPasswordOption());
+ expectedCredentialOptions.add(new GetPublicKeyCredentialOption("json"));
+
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferIdentityDocUi(true)
+ .build();
+
+ assertThat(request.getCredentialOptions()).hasSize(expectedCredentialOptions.size());
+ for (int i = 0; i < expectedCredentialOptions.size(); i++) {
+ assertThat(request.getCredentialOptions().get(i)).isEqualTo(
+ expectedCredentialOptions.get(i));
+ }
+ assertThat(request.getPreferIdentityDocUi()).isTrue();
+ }
+
+ @Test
+ public void builder_setPreferImmediatelyAvailableCredentials() {
+ ArrayList<CredentialOption> expectedCredentialOptions = new ArrayList<>();
+ expectedCredentialOptions.add(new GetPasswordOption());
+ expectedCredentialOptions.add(new GetPublicKeyCredentialOption("json"));
+ boolean expectedPreferImmediatelyAvailableCredentials = true;
+
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferImmediatelyAvailableCredentials(
+ expectedPreferImmediatelyAvailableCredentials)
+ .build();
+
+ assertThat(request.getCredentialOptions()).hasSize(expectedCredentialOptions.size());
+ for (int i = 0; i < expectedCredentialOptions.size(); i++) {
+ assertThat(request.getCredentialOptions().get(i)).isEqualTo(
+ expectedCredentialOptions.get(i));
+ }
+ assertThat(request.preferImmediatelyAvailableCredentials())
+ .isEqualTo(expectedPreferImmediatelyAvailableCredentials);
+ }
+
+ @Test
+ public void builder_setPreferUiBrandingComponentName() {
+ ArrayList<CredentialOption> expectedCredentialOptions = new ArrayList<>();
+ expectedCredentialOptions.add(new GetPasswordOption());
+ expectedCredentialOptions.add(new GetPublicKeyCredentialOption("json"));
+ ComponentName expectedComponentName = new ComponentName("test pkg", "test cls");
+
+ GetCredentialRequest request = new GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferUiBrandingComponentName(expectedComponentName)
+ .build();
+
+ assertThat(request.getCredentialOptions()).hasSize(expectedCredentialOptions.size());
+ for (int i = 0; i < expectedCredentialOptions.size(); i++) {
+ assertThat(request.getCredentialOptions().get(i)).isEqualTo(
+ expectedCredentialOptions.get(i));
+ }
+ assertThat(request.getPreferUiBrandingComponentName()).isEqualTo(expectedComponentName);
}
@Test
@@ -106,4 +205,30 @@
assertThat(request.getCredentialOptions().get(0).isAutoSelectAllowed()).isFalse();
}
+
+ @Test
+ public void frameworkConversion() {
+ ArrayList<CredentialOption> options = new ArrayList<>();
+ options.add(new GetPasswordOption());
+ boolean expectedPreferImmediatelyAvailableCredentials = true;
+ ComponentName expectedComponentName = new ComponentName("test pkg", "test cls");
+ boolean expectedPreferIdentityDocUi = true;
+ String expectedOrigin = "origin";
+ GetCredentialRequest request = new GetCredentialRequest(options, expectedOrigin,
+ expectedPreferIdentityDocUi, expectedComponentName,
+ expectedPreferImmediatelyAvailableCredentials);
+
+
+ GetCredentialRequest convertedRequest = GetCredentialRequest.createFrom(
+ options, request.getOrigin(), GetCredentialRequest.toRequestDataBundle(request)
+ );
+
+ assertThat(convertedRequest.getOrigin()).isEqualTo(expectedOrigin);
+ assertThat(convertedRequest.getPreferIdentityDocUi()).isEqualTo(
+ expectedPreferIdentityDocUi);
+ assertThat(convertedRequest.getPreferUiBrandingComponentName()).isEqualTo(
+ expectedComponentName);
+ assertThat(convertedRequest.preferImmediatelyAvailableCredentials()).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials);
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestTest.kt
index 1f477d4..cfc3aeb 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCredentialRequestTest.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.content.ComponentName
+import androidx.credentials.GetCredentialRequest.Companion.createFrom
+import androidx.credentials.GetCredentialRequest.Companion.toRequestDataBundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -53,6 +56,9 @@
)
}
assertThat(request.origin).isEqualTo(origin)
+ assertThat(request.preferIdentityDocUi).isFalse()
+ assertThat(request.preferImmediatelyAvailableCredentials).isFalse()
+ assertThat(request.preferUiBrandingComponentName).isNull()
}
@Test
@@ -65,6 +71,63 @@
assertThat(request.credentialOptions[0].isAutoSelectAllowed).isFalse()
assertThat(request.origin).isEqualTo(origin)
+ assertThat(request.preferIdentityDocUi).isFalse()
+ }
+
+ @Test
+ fun constructor_nonDefaultPreferUiBrandingComponentName() {
+ val options = java.util.ArrayList<CredentialOption>()
+ options.add(GetPasswordOption())
+ val expectedComponentName = ComponentName("test pkg", "test cls")
+
+ val request = GetCredentialRequest(
+ options, /*origin=*/null, /*preferIdentityDocUi=*/false, expectedComponentName
+ )
+
+ assertThat(request.credentialOptions[0].isAutoSelectAllowed).isFalse()
+ assertThat(request.preferUiBrandingComponentName).isEqualTo(expectedComponentName)
+ }
+
+ @Test
+ fun constructor_nonDefaultPreferImmediatelyAvailableCredentials() {
+ val options = java.util.ArrayList<CredentialOption>()
+ options.add(GetPasswordOption())
+ val expectedPreferImmediatelyAvailableCredentials = true
+
+ val request = GetCredentialRequest(
+ options,
+ origin = null,
+ preferIdentityDocUi = false,
+ preferUiBrandingComponentName = null,
+ expectedPreferImmediatelyAvailableCredentials
+ )
+
+ assertThat(request.credentialOptions[0].isAutoSelectAllowed).isFalse()
+ assertThat(request.preferImmediatelyAvailableCredentials)
+ .isEqualTo(expectedPreferImmediatelyAvailableCredentials)
+ }
+
+ @Test
+ fun builder_setPreferImmediatelyAvailableCredentials() {
+ val expectedCredentialOptions = java.util.ArrayList<CredentialOption>()
+ expectedCredentialOptions.add(GetPasswordOption())
+ expectedCredentialOptions.add(GetPublicKeyCredentialOption("json"))
+ val expectedPreferImmediatelyAvailableCredentials = true
+
+ val request = GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferImmediatelyAvailableCredentials(
+ expectedPreferImmediatelyAvailableCredentials
+ ).build()
+
+ assertThat(request.credentialOptions).hasSize(expectedCredentialOptions.size)
+ for (i in expectedCredentialOptions.indices) {
+ assertThat(request.credentialOptions[i]).isEqualTo(
+ expectedCredentialOptions[i]
+ )
+ }
+ assertThat(request.preferImmediatelyAvailableCredentials)
+ .isEqualTo(expectedPreferImmediatelyAvailableCredentials)
}
@Test
@@ -102,9 +165,52 @@
expectedCredentialOptions[i]
)
}
+ assertThat(request.preferIdentityDocUi).isFalse()
+ assertThat(request.preferImmediatelyAvailableCredentials).isFalse()
+ assertThat(request.preferUiBrandingComponentName).isNull()
}
@Test
+ fun builder_setPreferIdentityDocUis() {
+ val expectedCredentialOptions = ArrayList<CredentialOption>()
+ expectedCredentialOptions.add(GetPasswordOption())
+ expectedCredentialOptions.add(GetPublicKeyCredentialOption("json"))
+
+ val request = GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferIdentityDocUi(true)
+ .build()
+
+ assertThat(request.credentialOptions).hasSize(expectedCredentialOptions.size)
+ for (i in expectedCredentialOptions.indices) {
+ assertThat(request.credentialOptions[i]).isEqualTo(
+ expectedCredentialOptions[i]
+ )
+ }
+ assertThat(request.preferIdentityDocUi).isTrue()
+ }
+
+ @Test
+ fun builder_setPreferUiBrandingComponentName() {
+ val expectedCredentialOptions = java.util.ArrayList<CredentialOption>()
+ expectedCredentialOptions.add(GetPasswordOption())
+ expectedCredentialOptions.add(GetPublicKeyCredentialOption("json"))
+ val expectedComponentName = ComponentName("test pkg", "test cls")
+
+ val request = GetCredentialRequest.Builder()
+ .setCredentialOptions(expectedCredentialOptions)
+ .setPreferUiBrandingComponentName(expectedComponentName)
+ .build()
+
+ assertThat(request.credentialOptions).hasSize(expectedCredentialOptions.size)
+ for (i in expectedCredentialOptions.indices) {
+ assertThat(request.credentialOptions[i]).isEqualTo(
+ expectedCredentialOptions[i]
+ )
+ }
+ assertThat(request.preferUiBrandingComponentName).isEqualTo(expectedComponentName)
+ }
+ @Test
fun builder_defaultAutoSelect() {
val request = GetCredentialRequest.Builder()
.addCredentialOption(GetPasswordOption())
@@ -112,4 +218,34 @@
assertThat(request.credentialOptions[0].isAutoSelectAllowed).isFalse()
}
+
+ @Test
+ fun frameworkConversion() {
+ val options = java.util.ArrayList<CredentialOption>()
+ options.add(GetPasswordOption())
+ val expectedPreferImmediatelyAvailableCredentials = true
+ val expectedComponentName = ComponentName("test pkg", "test cls")
+ val expectedPreferIdentityDocUi = true
+ val expectedOrigin = "origin"
+ val request = GetCredentialRequest(
+ options, expectedOrigin,
+ expectedPreferIdentityDocUi, expectedComponentName,
+ expectedPreferImmediatelyAvailableCredentials
+ )
+
+ val convertedRequest = createFrom(
+ options, request.origin, toRequestDataBundle(request)
+ )
+
+ assertThat(convertedRequest.origin).isEqualTo(expectedOrigin)
+ assertThat(convertedRequest.preferIdentityDocUi).isEqualTo(
+ expectedPreferIdentityDocUi
+ )
+ assertThat(convertedRequest.preferUiBrandingComponentName).isEqualTo(
+ expectedComponentName
+ )
+ assertThat(convertedRequest.preferImmediatelyAvailableCredentials).isEqualTo(
+ expectedPreferImmediatelyAvailableCredentials
+ )
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionJavaTest.java
index 4d6de2d..b2c0494 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionJavaTest.java
@@ -20,14 +20,19 @@
import static org.junit.Assert.assertThrows;
+import android.content.ComponentName;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.google.common.collect.ImmutableSet;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GetCustomCredentialOptionJavaTest {
@@ -74,12 +79,17 @@
expectedCandidateQueryDataBundle.putBoolean("key", true);
boolean expectedSystemProvider = true;
boolean expectedAutoSelectAllowed = false;
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
GetCustomCredentialOption option = new GetCustomCredentialOption(expectedType,
expectedBundle,
expectedCandidateQueryDataBundle,
expectedSystemProvider,
- expectedAutoSelectAllowed);
+ expectedAutoSelectAllowed,
+ expectedAllowedProviders);
assertThat(option.getType()).isEqualTo(expectedType);
assertThat(TestUtilsKt.equals(option.getRequestData(), expectedBundle)).isTrue();
@@ -87,5 +97,43 @@
expectedCandidateQueryDataBundle)).isTrue();
assertThat(option.isAutoSelectAllowed()).isEqualTo(expectedAutoSelectAllowed);
assertThat(option.isSystemProviderRequired()).isEqualTo(expectedSystemProvider);
+ assertThat(option.getAllowedProviders())
+ .containsAtLeastElementsIn(expectedAllowedProviders);
+ }
+
+ @Test
+ public void frameworkConversion_success() {
+ String expectedType = "TYPE";
+ Bundle expectedBundle = new Bundle();
+ expectedBundle.putString("Test", "Test");
+ Bundle expectedCandidateQueryDataBundle = new Bundle();
+ expectedCandidateQueryDataBundle.putBoolean("key", true);
+ boolean expectedSystemProvider = true;
+ boolean expectedAutoSelectAllowed = false;
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+ GetCustomCredentialOption option = new GetCustomCredentialOption(expectedType,
+ expectedBundle,
+ expectedCandidateQueryDataBundle,
+ expectedSystemProvider,
+ expectedAutoSelectAllowed,
+ expectedAllowedProviders);
+
+ CredentialOption convertedOption = CredentialOption.createFrom(
+ option.getType(), option.getRequestData(), option.getCandidateQueryData(),
+ option.isSystemProviderRequired(), option.getAllowedProviders());
+
+ assertThat(convertedOption).isInstanceOf(GetCustomCredentialOption.class);
+ GetCustomCredentialOption actualOption = (GetCustomCredentialOption) convertedOption;
+ assertThat(actualOption.getType()).isEqualTo(expectedType);
+ assertThat(TestUtilsKt.equals(actualOption.getRequestData(), expectedBundle)).isTrue();
+ assertThat(TestUtilsKt.equals(actualOption.getCandidateQueryData(),
+ expectedCandidateQueryDataBundle)).isTrue();
+ assertThat(actualOption.isAutoSelectAllowed()).isEqualTo(expectedAutoSelectAllowed);
+ assertThat(actualOption.isSystemProviderRequired()).isEqualTo(expectedSystemProvider);
+ assertThat(actualOption.getAllowedProviders())
+ .containsAtLeastElementsIn(expectedAllowedProviders);
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionTest.kt
index 4910a54..8a3c0828 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetCustomCredentialOptionTest.kt
@@ -16,7 +16,9 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
+import androidx.credentials.CredentialOption.Companion.createFrom
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -56,15 +58,20 @@
expectedBundle.putString("Test", "Test")
val expectedCandidateQueryDataBundle = Bundle()
expectedCandidateQueryDataBundle.putBoolean("key", true)
- val expectedAutoSelectAllowed = false
+ val expectedAutoSelectAllowed = true
val expectedSystemProvider = true
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
val option = GetCustomCredentialOption(
expectedType,
expectedBundle,
expectedCandidateQueryDataBundle,
expectedSystemProvider,
- expectedAutoSelectAllowed
+ expectedAutoSelectAllowed,
+ expectedAllowedProviders
)
assertThat(option.type).isEqualTo(expectedType)
@@ -75,6 +82,52 @@
expectedCandidateQueryDataBundle
)
).isTrue()
+ assertThat(option.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
assertThat(option.isSystemProviderRequired).isEqualTo(expectedSystemProvider)
+ assertThat(option.allowedProviders)
+ .containsAtLeastElementsIn(expectedAllowedProviders)
+ }
+
+ @Test
+ fun frameworkConversion_success() {
+ val expectedType = "TYPE"
+ val expectedBundle = Bundle()
+ expectedBundle.putString("Test", "Test")
+ val expectedCandidateQueryDataBundle = Bundle()
+ expectedCandidateQueryDataBundle.putBoolean("key", true)
+ val expectedSystemProvider = true
+ val expectedAutoSelectAllowed = false
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val option = GetCustomCredentialOption(
+ expectedType,
+ expectedBundle,
+ expectedCandidateQueryDataBundle,
+ expectedSystemProvider,
+ expectedAutoSelectAllowed,
+ expectedAllowedProviders
+ )
+
+ val convertedOption = createFrom(
+ option.type, option.requestData, option.candidateQueryData,
+ option.isSystemProviderRequired, option.allowedProviders
+ )
+
+ assertThat(convertedOption).isInstanceOf(GetCustomCredentialOption::class.java)
+ val actualOption = convertedOption as GetCustomCredentialOption
+ assertThat(actualOption.type).isEqualTo(expectedType)
+ assertThat(equals(actualOption.requestData, expectedBundle)).isTrue()
+ assertThat(
+ equals(
+ actualOption.candidateQueryData,
+ expectedCandidateQueryDataBundle
+ )
+ ).isTrue()
+ assertThat(actualOption.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
+ assertThat(actualOption.isSystemProviderRequired).isEqualTo(expectedSystemProvider)
+ assertThat(actualOption.allowedProviders)
+ .containsAtLeastElementsIn(expectedAllowedProviders)
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
index b988bb3..16e278d 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
@@ -18,38 +18,98 @@
import static com.google.common.truth.Truth.assertThat;
-import android.os.Bundle;
+import android.content.ComponentName;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.google.common.collect.ImmutableSet;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GetPasswordOptionJavaTest {
@Test
- public void getter_frameworkProperties() {
+ public void emptyConstructor_success() {
GetPasswordOption option = new GetPasswordOption();
- Bundle expectedRequestDataBundle = new Bundle();
- expectedRequestDataBundle.putBoolean(GetPasswordOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
- false);
+
+ assertThat(option.isAutoSelectAllowed()).isFalse();
+ assertThat(option.getAllowedUserIds()).isEmpty();
+ assertThat(option.getAllowedProviders()).isEmpty();
+ }
+
+ @Test
+ public void construction_setOptionalValues_success() {
+ boolean expectedIsAutoSelectAllowed = true;
+ Set<String> expectedAllowedUserIds = ImmutableSet.of("id1", "id2", "id3");
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+
+ GetPasswordOption option = new GetPasswordOption(
+ expectedAllowedUserIds, expectedIsAutoSelectAllowed,
+ expectedAllowedProviders);
+
+ assertThat(option.isAutoSelectAllowed()).isEqualTo(expectedIsAutoSelectAllowed);
+ assertThat(option.getAllowedUserIds()).containsExactlyElementsIn(expectedAllowedUserIds);
+ assertThat(option.getAllowedProviders())
+ .containsExactlyElementsIn(expectedAllowedProviders);
+ }
+
+ @Test
+ public void getter_frameworkProperties() {
+ Set<String> expectedAllowedUserIds = ImmutableSet.of("id1", "id2", "id3");
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+ boolean expectedIsAutoSelectAllowed = true;
+
+ GetPasswordOption option = new GetPasswordOption(expectedAllowedUserIds,
+ expectedIsAutoSelectAllowed, expectedAllowedProviders);
assertThat(option.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
- assertThat(TestUtilsKt.equals(option.getRequestData(), expectedRequestDataBundle)).isTrue();
- assertThat(TestUtilsKt.equals(option.getCandidateQueryData(), Bundle.EMPTY)).isTrue();
+ assertThat(option.getRequestData().getBoolean(
+ CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)).isTrue();
+ assertThat(option.getRequestData().getStringArrayList(
+ GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS))
+ .containsExactlyElementsIn(expectedAllowedUserIds);
+ assertThat(option.getCandidateQueryData().getBoolean(
+ CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)).isTrue();
+ assertThat(option.getCandidateQueryData().getStringArrayList(
+ GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS))
+ .containsExactlyElementsIn(expectedAllowedUserIds);
assertThat(option.isSystemProviderRequired()).isFalse();
+ assertThat(option.getAllowedProviders())
+ .containsExactlyElementsIn(expectedAllowedProviders);
}
@Test
public void frameworkConversion_success() {
- GetPasswordOption option = new GetPasswordOption();
+ boolean expectedIsAutoSelectAllowed = true;
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+ Set<String> expectedAllowedUserIds = ImmutableSet.of("id1", "id2", "id3");
+ GetPasswordOption option = new GetPasswordOption(expectedAllowedUserIds,
+ expectedIsAutoSelectAllowed, expectedAllowedProviders);
CredentialOption convertedOption = CredentialOption.createFrom(
option.getType(), option.getRequestData(), option.getCandidateQueryData(),
- option.isSystemProviderRequired());
+ option.isSystemProviderRequired(),
+ option.getAllowedProviders());
assertThat(convertedOption).isInstanceOf(GetPasswordOption.class);
+ GetPasswordOption getPasswordOption = (GetPasswordOption) convertedOption;
+ assertThat(getPasswordOption.isAutoSelectAllowed()).isEqualTo(expectedIsAutoSelectAllowed);
+ assertThat(getPasswordOption.getAllowedProviders())
+ .containsExactlyElementsIn(expectedAllowedProviders);
+ assertThat(option.getAllowedUserIds()).containsExactlyElementsIn(expectedAllowedUserIds);
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
index 2a8c6d7..9af55946 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
@@ -16,8 +16,9 @@
package androidx.credentials
-import android.os.Bundle
+import android.content.ComponentName
import androidx.credentials.CredentialOption.Companion.createFrom
+import androidx.credentials.GetPasswordOption.Companion.BUNDLE_KEY_ALLOWED_USER_IDS
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -28,31 +29,92 @@
@SmallTest
class GetPasswordOptionTest {
@Test
- fun getter_frameworkProperties() {
+ fun emptyConstructor_success() {
val option = GetPasswordOption()
- val expectedRequestDataBundle = Bundle()
- expectedRequestDataBundle.putBoolean(
- CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
- false
+
+ assertThat(option.isAutoSelectAllowed).isFalse()
+ assertThat(option.allowedProviders).isEmpty()
+ assertThat(option.allowedUserIds).isEmpty()
+ }
+
+ @Test
+ fun construction_setOptionalValues_success() {
+ val expectedIsAutoSelectAllowed = true
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val expectedAllowedUserIds: Set<String> = setOf("id1", "id2", "id3")
+
+ val option = GetPasswordOption(
+ allowedUserIds = expectedAllowedUserIds,
+ isAutoSelectAllowed = expectedIsAutoSelectAllowed,
+ allowedProviders = expectedAllowedProviders,
+ )
+
+ assertThat(option.isAutoSelectAllowed).isEqualTo(expectedIsAutoSelectAllowed)
+ assertThat(option.allowedProviders)
+ .containsExactlyElementsIn(expectedAllowedProviders)
+ assertThat(option.allowedUserIds)
+ .containsExactlyElementsIn(expectedAllowedUserIds)
+ }
+
+ @Test
+ fun getter_frameworkProperties() {
+ val expectedAllowedUserIds: Set<String> = setOf("id1", "id2", "id3")
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val expectedIsAutoSelectAllowed = true
+
+ val option = GetPasswordOption(
+ allowedUserIds = expectedAllowedUserIds,
+ isAutoSelectAllowed = expectedIsAutoSelectAllowed,
+ allowedProviders = expectedAllowedProviders,
)
assertThat(option.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
- assertThat(equals(option.requestData, expectedRequestDataBundle)).isTrue()
- assertThat(equals(option.candidateQueryData, Bundle.EMPTY)).isTrue()
+ assertThat(option.requestData.getBoolean(
+ CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)).isTrue()
+ assertThat(option.requestData.getStringArrayList(
+ BUNDLE_KEY_ALLOWED_USER_IDS)).containsExactlyElementsIn(expectedAllowedUserIds)
+ assertThat(option.candidateQueryData.getBoolean(
+ CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)).isTrue()
+ assertThat(option.candidateQueryData.getStringArrayList(
+ BUNDLE_KEY_ALLOWED_USER_IDS)).containsExactlyElementsIn(expectedAllowedUserIds)
assertThat(option.isSystemProviderRequired).isFalse()
+ assertThat(option.allowedProviders)
+ .containsExactlyElementsIn(expectedAllowedProviders)
}
@Test
fun frameworkConversion_success() {
- val option = GetPasswordOption()
+ val expectedAllowedUserIds: Set<String> = setOf("id1", "id2", "id3")
+ val expectedAutoSelectAllowed = true
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val option = GetPasswordOption(
+ allowedUserIds = expectedAllowedUserIds,
+ isAutoSelectAllowed = expectedAutoSelectAllowed,
+ allowedProviders = expectedAllowedProviders,
+ )
val convertedOption = createFrom(
option.type,
option.requestData,
option.candidateQueryData,
- option.isSystemProviderRequired
+ option.isSystemProviderRequired,
+ option.allowedProviders
)
assertThat(convertedOption).isInstanceOf(GetPasswordOption::class.java)
+ assertThat(convertedOption.isAutoSelectAllowed).isEqualTo(expectedAutoSelectAllowed)
+ assertThat(convertedOption.allowedProviders)
+ .containsExactlyElementsIn(expectedAllowedProviders)
+ assertThat((convertedOption as GetPasswordOption).allowedUserIds)
+ .containsExactlyElementsIn(expectedAllowedUserIds)
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
index da4e145..42220a4 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
@@ -17,21 +17,25 @@
package androidx.credentials;
import static androidx.credentials.CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED;
-import static androidx.credentials.GetPublicKeyCredentialOption.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS;
import static androidx.credentials.GetPublicKeyCredentialOption.BUNDLE_KEY_REQUEST_JSON;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import android.content.ComponentName;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.google.common.collect.ImmutableSet;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -60,29 +64,6 @@
}
@Test
- public void constructor_setPreferImmediatelyAvailableCredentialsToFalseByDefault() {
- GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
- new GetPublicKeyCredentialOption(
- "JSON");
- boolean preferImmediatelyAvailableCredentialsActual =
- getPublicKeyCredentialOpt.preferImmediatelyAvailableCredentials();
- assertThat(preferImmediatelyAvailableCredentialsActual).isFalse();
- }
-
- @Test
- public void constructor_setPreferImmediatelyAvailableCredentialsToTrue() {
- boolean preferImmediatelyAvailableCredentialsExpected = true;
- String clientDataHash = "hash";
- GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
- new GetPublicKeyCredentialOption(
- "JSON", clientDataHash, preferImmediatelyAvailableCredentialsExpected);
- boolean preferImmediatelyAvailableCredentialsActual =
- getPublicKeyCredentialOpt.preferImmediatelyAvailableCredentials();
- assertThat(preferImmediatelyAvailableCredentialsActual).isEqualTo(
- preferImmediatelyAvailableCredentialsExpected);
- }
-
- @Test
public void getter_requestJson_success() {
String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
@@ -93,47 +74,53 @@
@Test
public void getter_frameworkProperties_success() {
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
- boolean preferImmediatelyAvailableCredentialsExpected = false;
boolean expectedIsAutoSelect = true;
- String clientDataHash = "hash";
+ byte[] clientDataHash = "hash".getBytes();
Bundle expectedData = new Bundle();
expectedData.putString(
PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
GetPublicKeyCredentialOption.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION);
expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
- expectedData.putString(GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
+ expectedData.putByteArray(GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
clientDataHash);
- expectedData.putBoolean(
- BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentialsExpected);
expectedData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, expectedIsAutoSelect);
GetPublicKeyCredentialOption option = new GetPublicKeyCredentialOption(
- requestJsonExpected, clientDataHash, preferImmediatelyAvailableCredentialsExpected);
+ requestJsonExpected, clientDataHash, expectedAllowedProviders);
assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
assertThat(TestUtilsKt.equals(option.getRequestData(), expectedData)).isTrue();
- expectedData.remove(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED);
assertThat(TestUtilsKt.equals(option.getCandidateQueryData(), expectedData)).isTrue();
assertThat(option.isSystemProviderRequired()).isFalse();
+ assertThat(option.getAllowedProviders())
+ .containsAtLeastElementsIn(expectedAllowedProviders);
}
@Test
public void frameworkConversion_success() {
- String clientDataHash = "hash";
- GetPublicKeyCredentialOption option =
- new GetPublicKeyCredentialOption("json", clientDataHash, true);
+ byte[] clientDataHash = "hash".getBytes();
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+ GetPublicKeyCredentialOption option = new GetPublicKeyCredentialOption(
+ "json", clientDataHash, expectedAllowedProviders);
CredentialOption convertedOption = CredentialOption.createFrom(
option.getType(), option.getRequestData(),
- option.getCandidateQueryData(), option.isSystemProviderRequired());
+ option.getCandidateQueryData(), option.isSystemProviderRequired(),
+ option.getAllowedProviders());
assertThat(convertedOption).isInstanceOf(GetPublicKeyCredentialOption.class);
GetPublicKeyCredentialOption convertedSubclassOption =
(GetPublicKeyCredentialOption) convertedOption;
assertThat(convertedSubclassOption.getRequestJson()).isEqualTo(option.getRequestJson());
- assertThat(convertedSubclassOption.preferImmediatelyAvailableCredentials()).isEqualTo(
- option.preferImmediatelyAvailableCredentials());
+ assertThat(convertedSubclassOption.getAllowedProviders())
+ .containsAtLeastElementsIn(expectedAllowedProviders);
}
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
index a4648fa..a270c7a4 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
import androidx.credentials.CredentialOption.Companion.createFrom
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -45,29 +46,6 @@
}
@Test
- fun constructor_setPreferImmediatelyAvailableCredentialsToFalseByDefault() {
- val getPublicKeyCredentialOpt = GetPublicKeyCredentialOption(
- "JSON"
- )
- val preferImmediatelyAvailableCredentialsActual =
- getPublicKeyCredentialOpt.preferImmediatelyAvailableCredentials
- assertThat(preferImmediatelyAvailableCredentialsActual).isFalse()
- }
-
- @Test
- fun constructor_setPreferImmediatelyAvailableCredentialsTrue() {
- val preferImmediatelyAvailableCredentialsExpected = true
- val clientDataHash = "hash"
- val getPublicKeyCredentialOpt = GetPublicKeyCredentialOption(
- "JSON", clientDataHash, preferImmediatelyAvailableCredentialsExpected
- )
- val preferImmediatelyAvailableCredentialsActual =
- getPublicKeyCredentialOpt.preferImmediatelyAvailableCredentials
- assertThat(preferImmediatelyAvailableCredentialsActual)
- .isEqualTo(preferImmediatelyAvailableCredentialsExpected)
- }
-
- @Test
fun getter_requestJson_success() {
val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
val createPublicKeyCredentialReq = GetPublicKeyCredentialOption(testJsonExpected)
@@ -78,9 +56,12 @@
@Test
fun getter_frameworkProperties_success() {
val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
- val preferImmediatelyAvailableCredentialsExpected = false
val expectedAutoSelectAllowed = true
- val clientDataHash = "hash"
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val clientDataHash = "hash".toByteArray()
val expectedData = Bundle()
expectedData.putString(
PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
@@ -90,39 +71,41 @@
GetPublicKeyCredentialOption.BUNDLE_KEY_REQUEST_JSON,
requestJsonExpected
)
- expectedData.putString(GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
+ expectedData.putByteArray(GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
clientDataHash)
expectedData.putBoolean(
- GetPublicKeyCredentialOption.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentialsExpected
- )
- expectedData.putBoolean(
CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED,
expectedAutoSelectAllowed
)
val option = GetPublicKeyCredentialOption(
- requestJsonExpected, clientDataHash, preferImmediatelyAvailableCredentialsExpected
+ requestJsonExpected, clientDataHash, expectedAllowedProviders
)
assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
assertThat(equals(option.requestData, expectedData)).isTrue()
- expectedData.remove(CredentialOption.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)
assertThat(equals(option.candidateQueryData, expectedData)).isTrue()
assertThat(option.isSystemProviderRequired).isFalse()
assertThat(option.isAutoSelectAllowed).isTrue()
+ assertThat(option.allowedProviders).containsAtLeastElementsIn(expectedAllowedProviders)
}
@Test
fun frameworkConversion_success() {
- val clientDataHash = "hash"
- val option = GetPublicKeyCredentialOption("json", clientDataHash, true)
+ val clientDataHash = "hash".toByteArray()
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+ val option = GetPublicKeyCredentialOption(
+ "json", clientDataHash, expectedAllowedProviders)
val convertedOption = createFrom(
option.type,
option.requestData,
option.candidateQueryData,
- option.isSystemProviderRequired
+ option.isSystemProviderRequired,
+ option.allowedProviders,
)
assertThat(convertedOption).isInstanceOf(
@@ -130,7 +113,7 @@
)
val convertedSubclassOption = convertedOption as GetPublicKeyCredentialOption
assertThat(convertedSubclassOption.requestJson).isEqualTo(option.requestJson)
- assertThat(convertedSubclassOption.preferImmediatelyAvailableCredentials)
- .isEqualTo(option.preferImmediatelyAvailableCredentials)
+ assertThat(convertedOption.allowedProviders)
+ .containsAtLeastElementsIn(expectedAllowedProviders)
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
index 659dd33..3bc767a 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
@@ -31,6 +31,13 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PasswordCredentialJavaTest {
+
+ @Test
+ public void typeConstant() {
+ assertThat(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ .isEqualTo("android.credentials.TYPE_PASSWORD_CREDENTIAL");
+ }
+
@Test
public void constructor_nullId_throws() {
assertThrows(
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
index 2c3f59d..7530833 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
@@ -28,6 +28,13 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class PasswordCredentialTest {
+
+ @Test
+ fun typeConstant() {
+ assertThat(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ .isEqualTo("android.credentials.TYPE_PASSWORD_CREDENTIAL")
+ }
+
@Test
fun constructor_emptyPassword_throws() {
assertThrows<IllegalArgumentException> {
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
index 3a63354..6fd3cd1 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
@@ -36,6 +36,12 @@
public class PublicKeyCredentialJavaTest {
@Test
+ public void typeConstant() {
+ assertThat(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ .isEqualTo("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL");
+ }
+
+ @Test
public void constructor_emptyJson_throwsIllegalArgumentException() {
assertThrows("Expected empty Json to throw IllegalArgumentException",
IllegalArgumentException.class,
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
index d701cae..17ff76c 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
@@ -33,6 +33,12 @@
class PublicKeyCredentialTest {
@Test
+ fun typeConstant() {
+ assertThat(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ .isEqualTo("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL")
+ }
+
+ @Test
fun constructor_emptyJson_throwsIllegalArgumentException() {
Assert.assertThrows(
"Expected empty Json to throw IllegalArgumentException",
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
index 440f3db..a69a475 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
@@ -16,8 +16,12 @@
package androidx.credentials
+import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
/** True if the two Bundles contain the same elements, and false otherwise. */
@Suppress("DEPRECATION")
@@ -68,7 +72,15 @@
/** True if the device running the test is post framework api level,
* false if pre framework api level. */
fun isPostFrameworkApiLevel(): Boolean {
- return !((Build.VERSION.SDK_INT <= MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL) &&
- !(Build.VERSION.SDK_INT == MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL &&
- Build.VERSION.PREVIEW_SDK_INT > 0))
+ return BuildCompat.isAtLeastU()
+}
+
+@RequiresApi(Build.VERSION_CODES.P)
+fun equals(a: Icon, b: Icon): Boolean {
+ return a.type == b.type && a.resId == b.resId
+}
+
+@RequiresApi(34)
+fun equals(a: CallingAppInfo, b: CallingAppInfo): Boolean {
+ return a.packageName == b.packageName && a.origin == b.origin
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseJavaTest.java
new file mode 100644
index 0000000..5373fb5
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseJavaTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static androidx.credentials.provider.ui.UiUtils.constructCreateEntryWithSimpleParams;
+import static androidx.credentials.provider.ui.UiUtils.constructRemoteEntry;
+import static androidx.credentials.provider.ui.UiUtils.constructRemoteEntryDefault;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.core.os.BuildCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginCreateCredentialResponseJavaTest {
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginCreateCredentialResponse(
+ Arrays.asList(constructCreateEntryWithSimpleParams("AccountName",
+ "Desc")),
+ null
+ );
+ }
+
+ @Test
+ public void builder_createEntriesOnly_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginCreateCredentialResponse.Builder().setCreateEntries(
+ Arrays.asList(constructCreateEntryWithSimpleParams("AccountName",
+ "Desc"))
+ ).build();
+ }
+
+ @Test
+ public void builder_remoteEntryOnly_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginCreateCredentialResponse.Builder().setRemoteEntry(
+ constructRemoteEntry()
+ ).build();
+ }
+
+ @Test
+ public void constructor_nullList_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginCreateCredentialResponse(
+ null, null)
+ );
+ }
+
+ @Test
+ public void buildConstruct_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginCreateCredentialResponse.Builder().setCreateEntries(
+ Arrays.asList(constructCreateEntryWithSimpleParams("AccountName",
+ "Desc"))).build();
+ }
+
+ @Test
+ public void buildConstruct_nullList_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginCreateCredentialResponse.Builder().setCreateEntries(null).build()
+ );
+ }
+
+ @Test
+ public void getter_createEntry() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedAccountName = "AccountName";
+ String expectedDescription = "Desc";
+
+ BeginCreateCredentialResponse response = new BeginCreateCredentialResponse(
+ Collections.singletonList(constructCreateEntryWithSimpleParams(expectedAccountName,
+ expectedDescription)), null);
+ String actualAccountName = response.getCreateEntries().get(0).getAccountName().toString();
+ String actualDescription = response.getCreateEntries().get(0).getDescription().toString();
+
+ assertThat(actualAccountName).isEqualTo(expectedAccountName);
+ assertThat(actualDescription).isEqualTo(expectedDescription);
+ }
+
+ @Test
+ public void getter_remoteEntry_null() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ RemoteEntry expectedRemoteEntry = null;
+ BeginCreateCredentialResponse response = new BeginCreateCredentialResponse(
+ Arrays.asList(constructCreateEntryWithSimpleParams("AccountName",
+ "Desc")),
+ expectedRemoteEntry
+ );
+ RemoteEntry actualRemoteEntry = response.getRemoteEntry();
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry);
+ }
+
+ @Test
+ public void getter_remoteEntry_nonNull() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ RemoteEntry expectedRemoteEntry = constructRemoteEntryDefault();
+
+ BeginCreateCredentialResponse response = new BeginCreateCredentialResponse(
+ Arrays.asList(constructCreateEntryWithSimpleParams("AccountName",
+ "Desc")),
+ expectedRemoteEntry
+ );
+ RemoteEntry actualRemoteEntry = response.getRemoteEntry();
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseTest.kt
new file mode 100644
index 0000000..675a6c9
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCredentialResponseTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.ui.UiUtils.Companion.constructCreateEntryWithSimpleParams
+import androidx.credentials.provider.ui.UiUtils.Companion.constructRemoteEntryDefault
+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
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BeginCreateCredentialResponseTest {
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ BeginCreateCredentialResponse(
+ createEntries = listOf(
+ constructCreateEntryWithSimpleParams(
+ "AccountName",
+ "Desc"
+ )
+ ),
+ remoteEntry = constructRemoteEntryDefault()
+ )
+ }
+
+ @Test
+ fun constructor_createEntriesOnly() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ BeginCreateCredentialResponse(
+ createEntries = listOf(
+ constructCreateEntryWithSimpleParams(
+ "AccountName",
+ "Desc"
+ )
+ )
+ )
+ }
+
+ @Test
+ fun constructor_remoteEntryOnly() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ BeginCreateCredentialResponse(
+ remoteEntry = constructRemoteEntryDefault()
+ )
+ }
+
+ @Test
+ fun getter_createEntry() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedAccountName = "AccountName"
+ val expectedDescription = "Desc"
+ val expectedSize = 1
+
+ val beginCreateCredentialResponse = BeginCreateCredentialResponse(
+ listOf(
+ constructCreateEntryWithSimpleParams(
+ expectedAccountName,
+ expectedDescription
+ )
+ ), null
+ )
+ val actualAccountName = beginCreateCredentialResponse.createEntries[0].accountName
+ val actualDescription = beginCreateCredentialResponse.createEntries[0].description
+
+ assertThat(beginCreateCredentialResponse.createEntries.size).isEqualTo(expectedSize)
+ assertThat(actualAccountName).isEqualTo(expectedAccountName)
+ assertThat(actualDescription).isEqualTo(expectedDescription)
+ }
+
+ @Test
+ fun getter_remoteEntry_null() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ val expectedRemoteEntry: RemoteEntry? = null
+ val beginCreateCredentialResponse = BeginCreateCredentialResponse(
+ listOf(
+ constructCreateEntryWithSimpleParams(
+ "AccountName",
+ "Desc"
+ )
+ ),
+ expectedRemoteEntry
+ )
+ val actualRemoteEntry = beginCreateCredentialResponse.remoteEntry
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry)
+ }
+
+ @Test
+ fun getter_remoteEntry_nonNull() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedRemoteEntry: RemoteEntry = constructRemoteEntryDefault()
+
+ val beginCreateCredentialResponse = BeginCreateCredentialResponse(
+ listOf(
+ constructCreateEntryWithSimpleParams(
+ "AccountName",
+ "Desc"
+ )
+ ),
+ expectedRemoteEntry
+ )
+ val actualRemoteEntry = beginCreateCredentialResponse.remoteEntry
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestJavaTest.java
new file mode 100644
index 0000000..d299d48
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestJavaTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.pm.SigningInfo;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.core.os.BuildCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginCreateCustomCredentialRequestJavaTest {
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginCreateCustomCredentialRequest("type", Bundle.EMPTY, null);
+ }
+
+ @Test
+ public void constructor_nullTypeBundle_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ // TODO(b/275416815) - parameterize to account for all individually
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginCreateCustomCredentialRequest(null, null, new CallingAppInfo(
+ "package", new SigningInfo()))
+ );
+ }
+
+ @Test
+ public void getter_type() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedType = "ironman";
+
+ BeginCreateCustomCredentialRequest beginCreateCustomCredentialRequest =
+ new BeginCreateCustomCredentialRequest(expectedType, Bundle.EMPTY, null);
+ String actualType = beginCreateCustomCredentialRequest.getType();
+
+ assertThat(actualType).isEqualTo(expectedType);
+ }
+
+ @Test
+ public void getter_bundle() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedKey = "query";
+ String expectedValue = "data";
+ Bundle expectedBundle = new Bundle();
+ expectedBundle.putString(expectedKey, expectedValue);
+
+ BeginCreateCustomCredentialRequest beginCreateCustomCredentialRequest =
+ new BeginCreateCustomCredentialRequest("type", expectedBundle, null);
+ Bundle actualBundle = beginCreateCustomCredentialRequest.getCandidateQueryData();
+
+ assertThat(actualBundle.getString(expectedKey)).isEqualTo(expectedValue);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestTest.kt
new file mode 100644
index 0000000..67d5605d
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreateCustomCredentialRequestTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.core.os.BuildCompat
+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
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BeginCreateCustomCredentialRequestTest {
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginCreateCustomCredentialRequest("type", Bundle.EMPTY, null)
+ }
+
+ @Test
+ fun getter_type() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedType = "ironman"
+ val beginCreateCustomCredentialRequest =
+ BeginCreateCustomCredentialRequest(expectedType, Bundle.EMPTY, null)
+ val actualType = beginCreateCustomCredentialRequest.type
+ assertThat(actualType).isEqualTo(expectedType)
+ }
+
+ @Test
+ fun getter_bundle() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedKey = "query"
+ val expectedValue = "data"
+ val expectedBundle = Bundle()
+ expectedBundle.putString(expectedKey, expectedValue)
+ val beginCreateCustomCredentialRequest =
+ BeginCreateCustomCredentialRequest("type", expectedBundle, null)
+ val actualBundle = beginCreateCustomCredentialRequest.candidateQueryData
+ assertThat(actualBundle.getString(expectedKey)).isEqualTo(expectedValue)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestJavaTest.java
new file mode 100644
index 0000000..e263c18
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestJavaTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.SigningInfo;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.TestUtilsKt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginCreatePasswordRequestJavaTest {
+ @Test
+ public void constructor_success() {
+ if (BuildCompat.isAtLeastU()) {
+ new BeginCreatePasswordCredentialRequest(
+ new CallingAppInfo("sample_package_name",
+ new SigningInfo()),
+ new Bundle());
+ }
+ }
+
+ @Test
+ public void getter_callingAppInfo() {
+ if (BuildCompat.isAtLeastU()) {
+ Bundle expectedCandidateQueryBundle = new Bundle();
+ expectedCandidateQueryBundle.putString("key", "value");
+ String expectedPackageName = "sample_package_name";
+ SigningInfo expectedSigningInfo = new SigningInfo();
+ CallingAppInfo expectedCallingAppInfo = new CallingAppInfo(expectedPackageName,
+ expectedSigningInfo);
+
+ BeginCreatePasswordCredentialRequest request =
+ new BeginCreatePasswordCredentialRequest(expectedCallingAppInfo,
+ expectedCandidateQueryBundle);
+
+ assertThat(request.getCallingAppInfo().getPackageName()).isEqualTo(expectedPackageName);
+ assertThat(request.getCallingAppInfo().getSigningInfo()).isEqualTo(expectedSigningInfo);
+ TestUtilsKt.equals(request.getCandidateQueryData(), expectedCandidateQueryBundle);
+ }
+ }
+
+ // TODO ("Add framework conversion, createFrom tests")
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestTest.kt
new file mode 100644
index 0000000..391842f
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePasswordRequestTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.content.pm.SigningInfo
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.equals
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RequiresApi(34)
+class BeginCreatePasswordRequestTest {
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginCreatePasswordCredentialRequest(
+ CallingAppInfo(
+ "sample_package_name",
+ SigningInfo()
+ ),
+ Bundle()
+ )
+ }
+
+ @Test
+ fun getter_callingAppInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ val expectedCandidateQueryBundle = Bundle()
+ expectedCandidateQueryBundle.putString("key", "value")
+ val expectedPackageName = "sample_package_name"
+ val expectedSigningInfo = SigningInfo()
+ val expectedCallingAppInfo = CallingAppInfo(
+ expectedPackageName,
+ expectedSigningInfo
+ )
+
+ val request = BeginCreatePasswordCredentialRequest(
+ expectedCallingAppInfo, expectedCandidateQueryBundle)
+
+ equals(request.candidateQueryData, expectedCandidateQueryBundle)
+ assertThat(request.callingAppInfo?.packageName).isEqualTo(expectedPackageName)
+ assertThat(request.callingAppInfo?.signingInfo).isEqualTo(expectedSigningInfo)
+ }
+
+ // TODO ("Add framework conversion, createFrom tests")
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestJavaTest.java
new file mode 100644
index 0000000..22308d7
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestJavaTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.pm.SigningInfo;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.core.os.BuildCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginCreatePublicKeyCredentialRequestJavaTest {
+ @Test
+ public void constructor_emptyJson_throwsIllegalArgumentException() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected empty Json to throw error",
+ IllegalArgumentException.class,
+ () -> new BeginCreatePublicKeyCredentialRequest(
+ "",
+ new CallingAppInfo(
+ "sample_package_name", new SigningInfo()),
+ new Bundle()
+ )
+ );
+ }
+ }
+
+ @Test
+ public void constructor_nullJson_throwsNullPointerException() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected null Json to throw NPE",
+ NullPointerException.class,
+ () -> new BeginCreatePublicKeyCredentialRequest(
+ null,
+ new CallingAppInfo("sample_package_name",
+ new SigningInfo()),
+ new Bundle()
+ )
+ );
+ }
+ }
+
+ @Test
+ public void constructor_success() {
+ if (BuildCompat.isAtLeastU()) {
+ new BeginCreatePublicKeyCredentialRequest(
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ new CallingAppInfo(
+ "sample_package_name", new SigningInfo()
+ ),
+ new Bundle()
+ );
+ }
+ }
+
+ @Test
+ public void constructorWithClientDataHash_success() {
+ if (BuildCompat.isAtLeastU()) {
+ new BeginCreatePublicKeyCredentialRequest(
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ new CallingAppInfo(
+ "sample_package_name", new SigningInfo()
+ ),
+ new Bundle(),
+ "client_data_hash".getBytes()
+ );
+ }
+ }
+
+ @Test
+ public void getter_requestJson_success() {
+ if (BuildCompat.isAtLeastU()) {
+ String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+
+ BeginCreatePublicKeyCredentialRequest
+ createPublicKeyCredentialReq = new BeginCreatePublicKeyCredentialRequest(
+ testJsonExpected,
+ new CallingAppInfo(
+ "sample_package_name", new SigningInfo()),
+ new Bundle()
+ );
+
+ String testJsonActual = createPublicKeyCredentialReq.getRequestJson();
+ assertThat(testJsonActual).isEqualTo(testJsonExpected);
+ assertThat(createPublicKeyCredentialReq.getClientDataHash()).isNull();
+
+ }
+ }
+
+ @Test
+ public void getter_clientDataHash_success() {
+ if (BuildCompat.isAtLeastU()) {
+ String testClientDataHashExpected = "client_data_hash";
+ BeginCreatePublicKeyCredentialRequest createPublicKeyCredentialReq =
+ new BeginCreatePublicKeyCredentialRequest(
+ "json",
+ new CallingAppInfo("sample_package_name",
+ new SigningInfo()),
+ new Bundle(),
+ testClientDataHashExpected.getBytes());
+
+ assertThat(createPublicKeyCredentialReq.getClientDataHash())
+ .isEqualTo(testClientDataHashExpected.getBytes());
+ }
+ }
+ // TODO ("Add framework conversion, createFrom & preferImmediatelyAvailable tests")
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestTest.kt
new file mode 100644
index 0000000..48b0416
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequestTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.content.pm.SigningInfo
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RequiresApi(34)
+class BeginCreatePublicKeyCredentialRequestTest {
+ @Test
+ fun constructor_emptyJson_throwsIllegalArgumentException() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ Assert.assertThrows(
+ "Expected empty Json to throw error",
+ IllegalArgumentException::class.java
+ ) {
+ BeginCreatePublicKeyCredentialRequest(
+ "",
+ CallingAppInfo(
+ "sample_package_name",
+ SigningInfo()
+ ),
+ Bundle()
+ )
+ }
+ }
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginCreatePublicKeyCredentialRequest(
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ CallingAppInfo(
+ "sample_package_name", SigningInfo()
+ ),
+ Bundle()
+ )
+ }
+
+ @Test
+ fun constructorWithClientDataHash_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginCreatePublicKeyCredentialRequest(
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ CallingAppInfo(
+ "sample_package_name", SigningInfo()
+ ),
+ Bundle(),
+ "client_data_hash".toByteArray()
+ )
+ }
+
+ @Test
+ fun getter_requestJson_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+
+ val createPublicKeyCredentialReq = BeginCreatePublicKeyCredentialRequest(
+ testJsonExpected,
+ CallingAppInfo(
+ "sample_package_name", SigningInfo()
+ ),
+ Bundle()
+ )
+
+ val testJsonActual = createPublicKeyCredentialReq.requestJson
+ assertThat(testJsonActual).isEqualTo(testJsonExpected)
+ assertThat(createPublicKeyCredentialReq.clientDataHash).isNull()
+ }
+
+ @Test
+ fun getter_clientDataHash_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val testClientDataHashExpected = "client_data_hash".toByteArray()
+ val createPublicKeyCredentialReq = BeginCreatePublicKeyCredentialRequest(
+ "json",
+ CallingAppInfo(
+ "sample_package_name", SigningInfo()
+ ),
+ Bundle(),
+ testClientDataHashExpected
+ )
+
+ val testClientDataHashActual = createPublicKeyCredentialReq.clientDataHash
+ assertThat(testClientDataHashActual).isEqualTo(testClientDataHashExpected)
+ }
+ // TODO ("Add framework conversion, createFrom & preferImmediatelyAvailable tests")
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestJavaTest.java
new file mode 100644
index 0000000..e593222
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestJavaTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.pm.SigningInfo;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.core.os.BuildCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginGetCredentialRequestJavaTest {
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginGetCredentialRequest(Collections.emptyList(), null);
+ }
+
+ @Test
+ public void constructor_nullList_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginGetCredentialRequest(null,
+ new CallingAppInfo("tom.cruise.security",
+ new SigningInfo()))
+ );
+ }
+
+ @Test
+ public void getter_beginGetCredentialOptions() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedKey = "query";
+ String expectedValue = "data";
+ Bundle expectedBundle = new Bundle();
+ expectedBundle.putString(expectedKey, expectedValue);
+ String expectedId = "key";
+ String expectedType = "mach-10";
+ int expectedBeginGetCredentialOptionsSize = 1;
+
+ BeginGetCredentialRequest beginGetCredentialRequest =
+ new BeginGetCredentialRequest(Collections.singletonList(
+ new BeginGetCustomCredentialOption(expectedId, expectedType,
+ expectedBundle)),
+ null);
+ List<BeginGetCredentialOption> actualBeginGetCredentialOptionList =
+ beginGetCredentialRequest.getBeginGetCredentialOptions();
+ int actualBeginGetCredentialOptionsSize = actualBeginGetCredentialOptionList.size();
+ assertThat(actualBeginGetCredentialOptionsSize)
+ .isEqualTo(expectedBeginGetCredentialOptionsSize);
+ String actualBundleValue =
+ actualBeginGetCredentialOptionList.get(0).getCandidateQueryData()
+ .getString(expectedKey);
+ String actualId = actualBeginGetCredentialOptionList.get(0).getId();
+ String actualType = actualBeginGetCredentialOptionList.get(0).getType();
+
+ assertThat(actualBundleValue).isEqualTo(expectedValue);
+ assertThat(actualId).isEqualTo(expectedId);
+ assertThat(actualType).isEqualTo(expectedType);
+ }
+
+ @Test
+ public void getter_nullCallingAppInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CallingAppInfo expectedCallingAppInfo = null;
+
+ BeginGetCredentialRequest beginGetCredentialRequest =
+ new BeginGetCredentialRequest(Collections.emptyList(),
+ expectedCallingAppInfo);
+ CallingAppInfo actualCallingAppInfo = beginGetCredentialRequest.getCallingAppInfo();
+
+ assertThat(actualCallingAppInfo).isEqualTo(expectedCallingAppInfo);
+ }
+
+ @Test
+ public void getter_nonNullCallingAppInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedPackageName = "john.wick.four.credentials";
+ CallingAppInfo expectedCallingAppInfo = new CallingAppInfo(expectedPackageName,
+ new SigningInfo());
+
+ BeginGetCredentialRequest beginGetCredentialRequest =
+ new BeginGetCredentialRequest(Collections.emptyList(),
+ expectedCallingAppInfo);
+ CallingAppInfo actualCallingAppInfo = beginGetCredentialRequest.getCallingAppInfo();
+ String actualPackageName = actualCallingAppInfo.getPackageName();
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestTest.kt
new file mode 100644
index 0000000..fbe6d828
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialRequestTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.content.pm.SigningInfo
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.core.os.BuildCompat
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class BeginGetCredentialRequestTest {
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginGetCredentialRequest(emptyList(), null)
+ }
+
+ @Test
+ fun getter_beginGetCredentialOptions() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedKey = "query"
+ val expectedValue = "data"
+ val expectedBundle = Bundle()
+ expectedBundle.putString(expectedKey, expectedValue)
+ val expectedId = "key"
+ val expectedType = "mach-10"
+ val expectedBeginGetCredentialOptionsSize = 1
+
+ val beginGetCredentialRequest = BeginGetCredentialRequest(
+ listOf(
+ BeginGetCustomCredentialOption(expectedId, expectedType, expectedBundle)
+ ),
+ null
+ )
+ val actualBeginGetCredentialOptionList = beginGetCredentialRequest.beginGetCredentialOptions
+ val actualBeginGetCredentialOptionsSize = actualBeginGetCredentialOptionList.size
+ assertThat(actualBeginGetCredentialOptionsSize)
+ .isEqualTo(expectedBeginGetCredentialOptionsSize)
+ val actualBundleValue = actualBeginGetCredentialOptionList[0].candidateQueryData
+ .getString(expectedKey)
+ val actualId = actualBeginGetCredentialOptionList[0].id
+ val actualType = actualBeginGetCredentialOptionList[0].type
+
+ assertThat(actualBundleValue).isEqualTo(expectedValue)
+ assertThat(actualId).isEqualTo(expectedId)
+ assertThat(actualType).isEqualTo(expectedType)
+ }
+
+ @Test
+ fun getter_nullCallingAppInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedCallingAppInfo: CallingAppInfo? = null
+
+ val beginGetCredentialRequest = BeginGetCredentialRequest(
+ emptyList(),
+ expectedCallingAppInfo
+ )
+ val actualCallingAppInfo = beginGetCredentialRequest.callingAppInfo
+
+ assertThat(actualCallingAppInfo).isEqualTo(expectedCallingAppInfo)
+ }
+
+ @Test
+ fun getter_nonNullCallingAppInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedPackageName = "john.wick.four.credentials"
+ val expectedCallingAppInfo = CallingAppInfo(
+ expectedPackageName,
+ SigningInfo()
+ )
+
+ val beginGetCredentialRequest = BeginGetCredentialRequest(
+ emptyList(),
+ expectedCallingAppInfo
+ )
+ val actualCallingAppInfo = beginGetCredentialRequest.callingAppInfo
+ val actualPackageName = actualCallingAppInfo!!.packageName
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseJavaTest.java
new file mode 100644
index 0000000..8be08e4
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseJavaTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static androidx.credentials.provider.ui.UiUtils.constructActionEntry;
+import static androidx.credentials.provider.ui.UiUtils.constructAuthenticationActionEntry;
+import static androidx.credentials.provider.ui.UiUtils.constructPasswordCredentialEntryDefault;
+import static androidx.credentials.provider.ui.UiUtils.constructRemoteEntryDefault;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.PasswordCredential;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginGetCredentialResponseJavaTest {
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginGetCredentialResponse();
+ }
+
+ // TODO(b/275416815) - parameterize to account for all individually
+ @Test
+ public void constructor_nullList_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginGetCredentialResponse(
+ null, null, null, constructRemoteEntryDefault())
+ );
+ }
+
+ @Test
+ public void buildConstruct_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new BeginGetCredentialResponse.Builder().build();
+ }
+
+ @Test
+ public void buildConstruct_nullList_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new BeginGetCredentialResponse.Builder().setCredentialEntries(null)
+ .setActions(null).setAuthenticationActions(null).build()
+ );
+ }
+
+ @Test
+ public void getter_credentialEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ int expectedSize = 1;
+ String expectedType = PasswordCredential.TYPE_PASSWORD_CREDENTIAL;
+ String expectedUsername = "f35";
+
+ BeginGetCredentialResponse response = new BeginGetCredentialResponse(
+ Collections.singletonList(constructPasswordCredentialEntryDefault(
+ expectedUsername)), Collections.emptyList(), Collections.emptyList(),
+ null);
+ int actualSize = response.getCredentialEntries().size();
+ String actualType = response.getCredentialEntries().get(0).getType();
+ String actualUsername = ((PasswordCredentialEntry) response.getCredentialEntries().get(0))
+ .getUsername().toString();
+
+ assertThat(actualSize).isEqualTo(expectedSize);
+ assertThat(actualType).isEqualTo(expectedType);
+ assertThat(actualUsername).isEqualTo(expectedUsername);
+ }
+
+ @Test
+ public void getter_actionEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ int expectedSize = 1;
+ String expectedTitle = "boeing";
+ String expectedSubtitle = "737max";
+
+ BeginGetCredentialResponse response = new BeginGetCredentialResponse(
+ Collections.emptyList(),
+ Collections.singletonList(constructActionEntry(expectedTitle, expectedSubtitle)),
+ Collections.emptyList(), null);
+ int actualSize = response.getActions().size();
+ String actualTitle = response.getActions().get(0).getTitle().toString();
+ String actualSubtitle = response.getActions().get(0).getSubtitle().toString();
+
+ assertThat(actualSize).isEqualTo(expectedSize);
+ assertThat(actualTitle).isEqualTo(expectedTitle);
+ assertThat(actualSubtitle).isEqualTo(expectedSubtitle);
+ }
+
+ @Test
+ public void getter_authActionEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ int expectedSize = 1;
+ String expectedTitle = "boeing";
+
+ BeginGetCredentialResponse response = new BeginGetCredentialResponse(
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.singletonList(constructAuthenticationActionEntry(expectedTitle)), null);
+ int actualSize = response.getAuthenticationActions().size();
+ String actualTitle = response.getAuthenticationActions().get(0).getTitle().toString();
+
+ assertThat(actualSize).isEqualTo(expectedSize);
+ assertThat(actualTitle).isEqualTo(expectedTitle);
+ }
+
+ @Test
+ public void getter_remoteEntry_null() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ RemoteEntry expectedRemoteEntry = null;
+
+ BeginGetCredentialResponse response = new BeginGetCredentialResponse(
+ Collections.emptyList(), Collections.emptyList(), Collections.emptyList(),
+ expectedRemoteEntry
+ );
+ RemoteEntry actualRemoteEntry = response.getRemoteEntry();
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry);
+ }
+
+ @Test
+ public void getter_remoteEntry_nonNull() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ RemoteEntry expectedRemoteEntry = constructRemoteEntryDefault();
+
+ BeginGetCredentialResponse response = new BeginGetCredentialResponse(
+ Collections.emptyList(), Collections.emptyList(), Collections.emptyList(),
+ expectedRemoteEntry
+ );
+ RemoteEntry actualRemoteEntry = response.getRemoteEntry();
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseTest.kt
new file mode 100644
index 0000000..c985c92
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCredentialResponseTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import androidx.core.os.BuildCompat
+import androidx.credentials.PasswordCredential
+import androidx.credentials.provider.ui.UiUtils.Companion.constructActionEntry
+import androidx.credentials.provider.ui.UiUtils.Companion.constructAuthenticationActionEntry
+import androidx.credentials.provider.ui.UiUtils.Companion.constructPasswordCredentialEntryDefault
+import androidx.credentials.provider.ui.UiUtils.Companion.constructRemoteEntryDefault
+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
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BeginGetCredentialResponseTest {
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ BeginGetCredentialResponse()
+ }
+
+ @Test
+ fun buildConstruct_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ BeginGetCredentialResponse.Builder().build()
+ }
+
+ @Test
+ fun getter_credentialEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedSize = 1
+ val expectedType = PasswordCredential.TYPE_PASSWORD_CREDENTIAL
+ val expectedUsername = "f35"
+
+ val response = BeginGetCredentialResponse(
+ listOf(
+ constructPasswordCredentialEntryDefault(
+ expectedUsername
+ )
+ )
+ )
+ val actualSize = response.credentialEntries.size
+ val actualType = response.credentialEntries[0].type
+ val actualUsername = (response.credentialEntries[0] as PasswordCredentialEntry)
+ .username.toString()
+
+ assertThat(actualSize).isEqualTo(expectedSize)
+ assertThat(actualType).isEqualTo(expectedType)
+ assertThat(actualUsername).isEqualTo(expectedUsername)
+ }
+
+ @Test
+ fun getter_actionEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedSize = 1
+ val expectedTitle = "boeing"
+ val expectedSubtitle = "737max"
+
+ val response = BeginGetCredentialResponse(
+ emptyList(),
+ listOf(constructActionEntry(expectedTitle, expectedSubtitle))
+ )
+ val actualSize = response.actions.size
+ val actualTitle = response.actions[0].title.toString()
+ val actualSubtitle = response.actions[0].subtitle.toString()
+
+ assertThat(actualSize).isEqualTo(expectedSize)
+ assertThat(actualTitle).isEqualTo(expectedTitle)
+ assertThat(actualSubtitle).isEqualTo(expectedSubtitle)
+ }
+
+ @Test
+ fun getter_authActionEntries() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedSize = 1
+ val expectedTitle = "boeing"
+
+ val response = BeginGetCredentialResponse(
+ emptyList(), emptyList(), listOf(
+ constructAuthenticationActionEntry(expectedTitle)
+ )
+ )
+ val actualSize = response.authenticationActions.size
+ val actualTitle = response.authenticationActions[0].title.toString()
+
+ assertThat(actualSize).isEqualTo(expectedSize)
+ assertThat(actualTitle).isEqualTo(expectedTitle)
+ }
+
+ @Test
+ fun getter_remoteEntry_null() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ val expectedRemoteEntry: RemoteEntry? = null
+ val response = BeginGetCredentialResponse(
+ emptyList(), emptyList(), emptyList(),
+ expectedRemoteEntry
+ )
+ val actualRemoteEntry = response.remoteEntry
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry)
+ }
+
+ @Test
+ fun getter_remoteEntry_nonNull() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ val expectedRemoteEntry = constructRemoteEntryDefault()
+ val response = BeginGetCredentialResponse(
+ emptyList(), emptyList(), emptyList(),
+ expectedRemoteEntry
+ )
+ val actualRemoteEntry = response.remoteEntry
+
+ assertThat(actualRemoteEntry).isEqualTo(expectedRemoteEntry)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionJavaTest.java
new file mode 100644
index 0000000..1394cac
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionJavaTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.TestUtilsKt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginGetCustomCredentialOptionJavaTest {
+ @Test
+ public void constructor_success() {
+ if (BuildCompat.isAtLeastU()) {
+ Bundle expectedBundle = new Bundle();
+ expectedBundle.putString("random", "random_value");
+ String expectedType = "type";
+ String expectedId = "id";
+
+ BeginGetCustomCredentialOption option = new BeginGetCustomCredentialOption(
+ expectedId, expectedType, expectedBundle);
+
+ assertThat(option.getType()).isEqualTo(expectedType);
+ assertThat(option.getId()).isEqualTo(expectedId);
+ assertThat(TestUtilsKt.equals(option.getCandidateQueryData(), expectedBundle)).isTrue();
+ }
+ }
+
+ @Test
+ public void constructor_emptyType_throwsIAE() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected empty Json to throw error",
+ IllegalArgumentException.class,
+ () -> new BeginGetCustomCredentialOption(
+ "id",
+ "",
+ new Bundle()
+ )
+ );
+ }
+ }
+
+ @Test
+ public void constructor_emptyId_throwsIAE() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected empty Json to throw error",
+ IllegalArgumentException.class,
+ () -> new BeginGetCustomCredentialOption(
+ "",
+ "type",
+ new Bundle()
+ )
+ );
+ }
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionTest.kt
new file mode 100644
index 0000000..f659b8c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetCustomCredentialOptionTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.core.os.BuildCompat
+import androidx.credentials.equals
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class BeginGetCustomCredentialOptionTest {
+ @Test
+ fun constructor_success() {
+ if (BuildCompat.isAtLeastU()) {
+ val expectedBundle = Bundle()
+ expectedBundle.putString("random", "random_value")
+ val expectedType = "type"
+ val expectedId = "id"
+ val option = BeginGetCustomCredentialOption(
+ expectedId, expectedType, expectedBundle
+ )
+ Truth.assertThat(option.type).isEqualTo(expectedType)
+ Truth.assertThat(option.id).isEqualTo(expectedId)
+ Truth.assertThat(equals(option.candidateQueryData, expectedBundle)).isTrue()
+ }
+ }
+
+ @Test
+ fun constructor_emptyType_throwsIAE() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows(
+ "Expected empty Json to throw error",
+ IllegalArgumentException::class.java
+ ) {
+ BeginGetCustomCredentialOption(
+ "id",
+ "",
+ Bundle()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun constructor_emptyId_throwsIAE() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows(
+ "Expected empty Json to throw error",
+ IllegalArgumentException::class.java
+ ) {
+ BeginGetCustomCredentialOption(
+ "",
+ "type",
+ Bundle()
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionJavaTest.java
new file mode 100644
index 0000000..f73d7a0
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionJavaTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.GetPasswordOption;
+import androidx.credentials.PasswordCredential;
+import androidx.credentials.TestUtilsKt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginGetPasswordOptionJavaTest {
+ private static final String BUNDLE_ID_KEY =
+ "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY";
+ private static final String BUNDLE_ID = "id";
+ @Test
+ public void getter_frameworkProperties() {
+ if (BuildCompat.isAtLeastU()) {
+ Set<String> expectedAllowedUserIds = ImmutableSet.of("id1", "id2", "id3");
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList(GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS,
+ new ArrayList<>(expectedAllowedUserIds));
+
+ BeginGetPasswordOption option = new BeginGetPasswordOption(expectedAllowedUserIds,
+ bundle, BUNDLE_ID);
+
+ bundle.putString(BUNDLE_ID_KEY, BUNDLE_ID);
+ assertThat(option.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(option.getCandidateQueryData(), bundle)).isTrue();
+ assertThat(option.getAllowedUserIds())
+ .containsExactlyElementsIn(expectedAllowedUserIds);
+ }
+ }
+
+ // TODO ("Add framework conversion, createFrom tests")
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionTest.kt
new file mode 100644
index 0000000..1fd619e
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPasswordOptionTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.GetPasswordOption
+import androidx.credentials.PasswordCredential
+import androidx.credentials.equals
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RequiresApi(34)
+class BeginGetPasswordOptionTest {
+ companion object {
+ private const val BUNDLE_ID_KEY =
+ "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY"
+ private const val BUNDLE_ID = "id"
+ }
+ @Test
+ fun getter_frameworkProperties() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedAllowedUserIds: Set<String> = setOf("id1", "id2", "id3")
+ val bundle = Bundle()
+ bundle.putStringArrayList(
+ GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS,
+ ArrayList(expectedAllowedUserIds)
+ )
+
+ val option = BeginGetPasswordOption(expectedAllowedUserIds, bundle, BUNDLE_ID)
+
+ bundle.putString(BUNDLE_ID_KEY, BUNDLE_ID)
+ assertThat(option.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ assertThat(equals(option.candidateQueryData, bundle)).isTrue()
+ assertThat(option.allowedUserIds).containsExactlyElementsIn(expectedAllowedUserIds)
+ }
+
+ // TODO ("Add framework conversion, createFrom tests")
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionJavaTest.java
new file mode 100644
index 0000000..e0ff56e
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionJavaTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static androidx.credentials.GetPublicKeyCredentialOption.BUNDLE_KEY_REQUEST_JSON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.GetPublicKeyCredentialOption;
+import androidx.credentials.PublicKeyCredential;
+import androidx.credentials.TestUtilsKt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BeginGetPublicKeyCredentialOptionJavaTest {
+ private static final String BUNDLE_ID_KEY =
+ "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY";
+ private static final String BUNDLE_ID = "id";
+ @Test
+ public void constructor_emptyJson_throwsIllegalArgumentException() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected empty Json to throw error",
+ IllegalArgumentException.class,
+ () -> new BeginGetPublicKeyCredentialOption(
+ new Bundle(), "", "")
+ );
+ }
+ }
+
+ @Test
+ public void constructor_nullJson_throwsNullPointerException() {
+ if (BuildCompat.isAtLeastU()) {
+ assertThrows("Expected null Json to throw NPE",
+ NullPointerException.class,
+ () -> new BeginGetPublicKeyCredentialOption(
+ new Bundle(), BUNDLE_ID, null)
+ );
+ }
+ }
+
+ @Test
+ public void constructor_success() {
+ if (BuildCompat.isAtLeastU()) {
+ new BeginGetPublicKeyCredentialOption(
+ new Bundle(), BUNDLE_ID,
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}");
+ }
+ }
+
+ @Test
+ public void constructorWithClientDataHash_success() {
+ if (BuildCompat.isAtLeastU()) {
+ new BeginGetPublicKeyCredentialOption(
+ new Bundle(), BUNDLE_ID,
+ "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ "client_data_hash".getBytes());
+ }
+ }
+
+ @Test
+ public void getter_requestJson_success() {
+ if (BuildCompat.isAtLeastU()) {
+ String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+
+ BeginGetPublicKeyCredentialOption getPublicKeyCredentialOpt =
+ new BeginGetPublicKeyCredentialOption(
+ new Bundle(), BUNDLE_ID, testJsonExpected);
+
+ String testJsonActual = getPublicKeyCredentialOpt.getRequestJson();
+ assertThat(testJsonActual).isEqualTo(testJsonExpected);
+ }
+ }
+
+ @Test
+ public void getter_clientDataHash_success() {
+ if (BuildCompat.isAtLeastU()) {
+ byte[] testClientDataHashExpected = "client_data_hash".getBytes();
+
+ BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOpt =
+ new BeginGetPublicKeyCredentialOption(
+ new Bundle(), BUNDLE_ID, "test_json",
+ testClientDataHashExpected);
+
+ byte[] testClientDataHashActual = beginGetPublicKeyCredentialOpt.getClientDataHash();
+ assertThat(testClientDataHashActual).isEqualTo(testClientDataHashExpected);
+ }
+ }
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ if (BuildCompat.isAtLeastU()) {
+ String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ byte[] clientDataHash = "client_data_hash".getBytes();
+ Bundle expectedData = new Bundle();
+ expectedData.putString(
+ PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
+ GetPublicKeyCredentialOption
+ .BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION);
+ expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+ expectedData.putByteArray(GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
+ clientDataHash);
+
+ BeginGetPublicKeyCredentialOption option = new BeginGetPublicKeyCredentialOption(
+ expectedData, BUNDLE_ID, requestJsonExpected, clientDataHash);
+
+ expectedData.putString(BUNDLE_ID_KEY, BUNDLE_ID);
+ assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(option.getCandidateQueryData(), expectedData)).isTrue();
+ }
+ }
+ // TODO ("Add framework conversion, createFrom tests")
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionTest.kt
new file mode 100644
index 0000000..66cb74d
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOptionTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.GetPublicKeyCredentialOption
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.equals
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RequiresApi(34)
+class BeginGetPublicKeyCredentialOptionTest {
+ companion object {
+ private const val BUNDLE_ID_KEY =
+ "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY"
+ private const val BUNDLE_ID = "id"
+ }
+ @Test
+ fun constructor_emptyJson_throwsIllegalArgumentException() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ Assert.assertThrows(
+ "Expected empty Json to throw error",
+ IllegalArgumentException::class.java
+ ) {
+ BeginGetPublicKeyCredentialOption(Bundle(), "", "")
+ }
+ }
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ BeginGetPublicKeyCredentialOption(
+ Bundle(), BUNDLE_ID, "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+ "client_data_hash".toByteArray()
+ )
+ }
+
+ @Test
+ fun getter_clientDataHash_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val testClientDataHashExpected = "client_data_hash".toByteArray()
+
+ val beginGetPublicKeyCredentialOpt = BeginGetPublicKeyCredentialOption(
+ Bundle(), BUNDLE_ID, "test_json", testClientDataHashExpected
+ )
+
+ val testClientDataHashActual = beginGetPublicKeyCredentialOpt.clientDataHash
+ assertThat(testClientDataHashActual).isEqualTo(testClientDataHashExpected)
+ }
+
+ @Test
+ fun getter_requestJson_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+
+ val getPublicKeyCredentialOpt = BeginGetPublicKeyCredentialOption(
+ Bundle(), BUNDLE_ID, testJsonExpected
+ )
+
+ val testJsonActual = getPublicKeyCredentialOpt.requestJson
+ assertThat(testJsonActual).isEqualTo(testJsonExpected)
+ }
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val clientDataHash = "client_data_hash".toByteArray()
+ val expectedData = Bundle()
+ expectedData.putString(
+ PublicKeyCredential.BUNDLE_KEY_SUBTYPE,
+ GetPublicKeyCredentialOption.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION)
+ expectedData.putString(
+ GetPublicKeyCredentialOption.BUNDLE_KEY_REQUEST_JSON,
+ requestJsonExpected)
+ expectedData.putByteArray(
+ GetPublicKeyCredentialOption.BUNDLE_KEY_CLIENT_DATA_HASH,
+ clientDataHash)
+
+ val option = BeginGetPublicKeyCredentialOption(expectedData, BUNDLE_ID, requestJsonExpected)
+
+ expectedData.putString(BUNDLE_ID_KEY, BUNDLE_ID)
+ assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(option.candidateQueryData, expectedData)).isTrue()
+ }
+
+ // TODO ("Add framework conversion, createFrom tests")
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestJavaTest.java
new file mode 100644
index 0000000..ec0620c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestJavaTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.pm.SigningInfo;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.credentials.TestUtilsKt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProviderClearCredentialStateRequestJavaTest {
+
+ @Test
+ public void testConstructor_success() {
+ CallingAppInfo callingAppInfo = new CallingAppInfo(
+ "package", new SigningInfo());
+
+ ProviderClearCredentialStateRequest request = new
+ ProviderClearCredentialStateRequest(callingAppInfo);
+
+ assertThat(TestUtilsKt.equals(request.getCallingAppInfo(), callingAppInfo))
+ .isTrue();
+ }
+
+ @Test
+ public void testConstructor_nullCallingAppInfo_throwsNPE() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new ProviderClearCredentialStateRequest(null)
+ );
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestTest.kt
new file mode 100644
index 0000000..d2ed5da
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderClearCredentialStateRequestTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.content.pm.SigningInfo
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.RequiresApi
+import androidx.credentials.equals
+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
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ProviderClearCredentialStateRequestTest {
+
+ @RequiresApi(34)
+ @Test
+ fun testConstructor_success() {
+ val callingAppInfo = CallingAppInfo("sample_package_name", SigningInfo())
+
+ val request = ProviderClearCredentialStateRequest(callingAppInfo)
+
+ assertThat(equals(callingAppInfo, request.callingAppInfo)).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestJavaTest.java
new file mode 100644
index 0000000..79d8421
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestJavaTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.ComponentName;
+import android.content.pm.SigningInfo;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.CredentialOption;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProviderGetCredentialRequestJavaTest {
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ new ProviderGetCredentialRequest(
+ Collections.singletonList(CredentialOption.createFrom("type", new Bundle(),
+ new Bundle(), true, ImmutableSet.of())),
+ new CallingAppInfo("name",
+ new SigningInfo()));
+ }
+
+ @Test
+ public void constructor_nullInputs_throws() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+
+ assertThrows("Expected null list to throw NPE",
+ NullPointerException.class,
+ () -> new ProviderGetCredentialRequest(null, null)
+ );
+ }
+
+ @Test
+ public void getter_credentialOptions() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedType = "BoeingCred";
+ String expectedQueryKey = "PilotName";
+ String expectedQueryValue = "PilotPassword";
+ Bundle expectedCandidateQueryData = new Bundle();
+ expectedCandidateQueryData.putString(expectedQueryKey, expectedQueryValue);
+ String expectedRequestKey = "PlaneKey";
+ String expectedRequestValue = "PlaneInfo";
+ Bundle expectedRequestData = new Bundle();
+ expectedRequestData.putString(expectedRequestKey, expectedRequestValue);
+ boolean expectedRequireSystemProvider = true;
+ Set<ComponentName> expectedAllowedProviders = ImmutableSet.of(
+ new ComponentName("pkg", "cls"),
+ new ComponentName("pkg2", "cls2")
+ );
+
+ ProviderGetCredentialRequest providerGetCredentialRequest =
+ new ProviderGetCredentialRequest(
+ Collections.singletonList(CredentialOption.createFrom(expectedType,
+ expectedRequestData,
+ expectedCandidateQueryData,
+ expectedRequireSystemProvider,
+ expectedAllowedProviders)),
+ new CallingAppInfo("name",
+ new SigningInfo()));
+ List<CredentialOption> actualCredentialOptionsList =
+ providerGetCredentialRequest.getCredentialOptions();
+ assertThat(actualCredentialOptionsList.size()).isEqualTo(1);
+ String actualType = actualCredentialOptionsList.get(0).getType();
+ String actualRequestValue =
+ actualCredentialOptionsList.get(0).getRequestData().getString(expectedRequestKey);
+ String actualQueryValue =
+ actualCredentialOptionsList.get(0).getCandidateQueryData()
+ .getString(expectedQueryKey);
+ boolean actualRequireSystemProvider =
+ actualCredentialOptionsList.get(0).isSystemProviderRequired();
+
+ assertThat(actualType).isEqualTo(expectedType);
+ assertThat(actualRequestValue).isEqualTo(expectedRequestValue);
+ assertThat(actualQueryValue).isEqualTo(expectedQueryValue);
+ assertThat(actualRequireSystemProvider).isEqualTo(expectedRequireSystemProvider);
+ assertThat(actualCredentialOptionsList.get(0).getAllowedProviders())
+ .containsAtLeastElementsIn(expectedAllowedProviders);
+ }
+
+ @Test
+ public void getter_signingInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ String expectedPackageName = "cool.security.package";
+
+ ProviderGetCredentialRequest providerGetCredentialRequest =
+ new ProviderGetCredentialRequest(
+ Collections.singletonList(CredentialOption.createFrom("type", new Bundle(),
+ new Bundle(), true, ImmutableSet.of())),
+ new CallingAppInfo(expectedPackageName,
+ new SigningInfo()));
+ String actualPackageName =
+ providerGetCredentialRequest.getCallingAppInfo().getPackageName();
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestTest.kt
new file mode 100644
index 0000000..8e5f0ca
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ProviderGetCredentialRequestTest.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.content.ComponentName
+import android.content.pm.SigningInfo
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.core.os.BuildCompat
+import androidx.credentials.CredentialOption.Companion.createFrom
+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
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ProviderGetCredentialRequestTest {
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+
+ ProviderGetCredentialRequest(
+ listOf(
+ createFrom(
+ "type", Bundle(),
+ Bundle(), true,
+ emptySet()
+ )
+ ), CallingAppInfo(
+ "name",
+ SigningInfo()
+ )
+ )
+ }
+
+ // TODO(b/275416815) - Test createFrom()
+
+ @Test
+ fun getter_credentialOptions() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedType = "BoeingCred"
+ val expectedQueryKey = "PilotName"
+ val expectedQueryValue = "PilotPassword"
+ val expectedCandidateQueryData = Bundle()
+ expectedCandidateQueryData.putString(expectedQueryKey, expectedQueryValue)
+ val expectedRequestKey = "PlaneKey"
+ val expectedRequestValue = "PlaneInfo"
+ val expectedRequestData = Bundle()
+ expectedRequestData.putString(expectedRequestKey, expectedRequestValue)
+ val expectedRequireSystemProvider = true
+ val expectedAllowedProviders: Set<ComponentName> = setOf(
+ ComponentName("pkg", "cls"),
+ ComponentName("pkg2", "cls2")
+ )
+
+ val providerGetCredentialRequest = ProviderGetCredentialRequest(
+ listOf(
+ createFrom(
+ expectedType,
+ expectedRequestData,
+ expectedCandidateQueryData,
+ expectedRequireSystemProvider,
+ expectedAllowedProviders
+ )
+ ),
+ CallingAppInfo(
+ "name",
+ SigningInfo()
+ )
+ )
+ val actualCredentialOptionsList = providerGetCredentialRequest.credentialOptions
+ assertThat(actualCredentialOptionsList.size).isEqualTo(1)
+ val actualType = actualCredentialOptionsList[0].type
+ val actualRequestValue =
+ actualCredentialOptionsList[0].requestData.getString(expectedRequestKey)
+ val actualQueryValue = actualCredentialOptionsList[0].candidateQueryData
+ .getString(expectedQueryKey)
+ val actualRequireSystemProvider = actualCredentialOptionsList[0].isSystemProviderRequired
+
+ assertThat(actualType).isEqualTo(expectedType)
+ assertThat(actualRequestValue).isEqualTo(expectedRequestValue)
+ assertThat(actualQueryValue).isEqualTo(expectedQueryValue)
+ assertThat(actualRequireSystemProvider).isEqualTo(expectedRequireSystemProvider)
+ assertThat(actualCredentialOptionsList[0].allowedProviders)
+ .containsAtLeastElementsIn(expectedAllowedProviders)
+ }
+
+ @Test
+ fun getter_signingInfo() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val expectedPackageName = "cool.security.package"
+
+ val providerGetCredentialRequest = ProviderGetCredentialRequest(
+ listOf(
+ createFrom(
+ "type", Bundle(),
+ Bundle(), true, emptySet()
+ )
+ ), CallingAppInfo(
+ expectedPackageName,
+ SigningInfo()
+ )
+ )
+ val actualPackageName = providerGetCredentialRequest.callingAppInfo.packageName
+
+ assertThat(actualPackageName).isEqualTo(expectedPackageName)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java
new file mode 100644
index 0000000..d0bc879
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.provider.Action;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class ActionJavaTest {
+ private static final CharSequence TITLE = "title";
+ private static final CharSequence SUBTITLE = "subtitle";
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ Action action = new Action(TITLE, mPendingIntent, SUBTITLE);
+
+ assertNotNull(action);
+ assertThat(TITLE.equals(action.getTitle()));
+ assertThat(SUBTITLE.equals(action.getSubtitle()));
+ assertThat(mPendingIntent == action.getPendingIntent());
+ }
+
+ @Test
+ public void constructor_nullTitle_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null title to throw NPE",
+ NullPointerException.class,
+ () -> new Action(null, mPendingIntent, SUBTITLE));
+ }
+
+ @Test
+ public void constructor_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null title to throw NPE",
+ NullPointerException.class,
+ () -> new Action(TITLE, null, SUBTITLE));
+ }
+
+ @Test
+ public void constructor_emptyTitle_throwsIllegalArgumentException() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty title to throw IllegalArgumentException",
+ IllegalArgumentException.class,
+ () -> new Action("", mPendingIntent, SUBTITLE));
+ }
+
+ @Test
+ public void fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ Action originalAction = new Action(TITLE, mPendingIntent, SUBTITLE);
+ Slice slice = Action.toSlice(originalAction);
+
+ Action fromSlice = Action.fromSlice(slice);
+
+ assertNotNull(fromSlice);
+ assertThat(fromSlice.getTitle()).isEqualTo(TITLE);
+ assertThat(fromSlice.getSubtitle()).isEqualTo(SUBTITLE);
+ assertThat(fromSlice.getPendingIntent()).isEqualTo(mPendingIntent);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionTest.kt
new file mode 100644
index 0000000..c075468
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.Action.Companion.fromSlice
+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 org.junit.Assert
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+class ActionTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val action = Action(TITLE, mPendingIntent, SUBTITLE)
+ val slice = Action.toSlice(action)
+
+ assertNotNull(action)
+ assertNotNull(slice)
+ assertThat(TITLE == action.title)
+ assertThat(SUBTITLE == action.subtitle)
+ assertThat(mPendingIntent === action.pendingIntent)
+ }
+
+ @Test
+ fun constructor_emptyTitle_throwsIllegalArgumentException() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ Assert.assertThrows(
+ "Expected empty title to throw IllegalArgumentException",
+ IllegalArgumentException::class.java
+ ) { Action("", mPendingIntent, SUBTITLE) }
+ }
+
+ @Test
+ fun fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalAction = Action(TITLE, mPendingIntent, SUBTITLE)
+ val slice = Action.toSlice(originalAction)
+
+ val fromSlice = fromSlice(slice)
+
+ assertNotNull(fromSlice)
+ fromSlice?.let {
+ assertThat(fromSlice.title).isEqualTo(TITLE)
+ assertThat(fromSlice.subtitle).isEqualTo(SUBTITLE)
+ assertThat(fromSlice.pendingIntent).isEqualTo(mPendingIntent)
+ }
+ }
+
+ companion object {
+ private val TITLE: CharSequence = "title"
+ private val SUBTITLE: CharSequence = "subtitle"
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java
new file mode 100644
index 0000000..1d2f090
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.provider.AuthenticationAction;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class AuthenticationActionJavaTest {
+ private static final CharSequence TITLE = "title";
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent, PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ AuthenticationAction action = new AuthenticationAction(TITLE, mPendingIntent);
+
+ assertThat(mPendingIntent == action.getPendingIntent());
+ }
+
+ @Test
+ public void constructor_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new AuthenticationAction(TITLE, null));
+ }
+
+ @Test
+ public void fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ AuthenticationAction originalAction = new AuthenticationAction(TITLE, mPendingIntent);
+ Slice slice = AuthenticationAction.toSlice(originalAction);
+
+ AuthenticationAction fromSlice = AuthenticationAction.fromSlice(slice);
+
+ assertNotNull(fromSlice);
+ assertThat(fromSlice.getPendingIntent()).isEqualTo(mPendingIntent);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionTest.kt
new file mode 100644
index 0000000..74fbecd
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.AuthenticationAction.Companion.fromSlice
+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 org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+class AuthenticationActionTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val action = AuthenticationAction(TITLE, mPendingIntent)
+
+ assertThat(mPendingIntent).isEqualTo(action.pendingIntent)
+ }
+
+ @Test
+ fun fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalAction = AuthenticationAction(TITLE, mPendingIntent)
+ val slice = AuthenticationAction.toSlice(originalAction)
+
+ val fromSlice = fromSlice(slice)
+
+ assertNotNull(fromSlice)
+ fromSlice?.let {
+ assertNotNull(fromSlice.pendingIntent)
+ assertThat(fromSlice.pendingIntent).isEqualTo(mPendingIntent)
+ }
+ }
+
+ companion object {
+ private val TITLE: CharSequence = "title"
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java
new file mode 100644
index 0000000..f2992da
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.provider.CreateEntry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class CreateEntryJavaTest {
+ private static final CharSequence ACCOUNT_NAME = "account_name";
+ private static final int PASSWORD_COUNT = 10;
+ private static final int PUBLIC_KEY_CREDENTIAL_COUNT = 10;
+ private static final int TOTAL_COUNT = 10;
+
+ private static final Long LAST_USED_TIME = 10L;
+ private static final Icon ICON = Icon.createWithBitmap(Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888));
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void constructor_requiredParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CreateEntry entry = constructEntryWithRequiredParams();
+
+ assertNotNull(entry);
+ assertEntryWithRequiredParams(entry);
+ assertNull(entry.getIcon());
+ assertNull(entry.getLastUsedTime());
+ assertNull(entry.getPasswordCredentialCount());
+ assertNull(entry.getPublicKeyCredentialCount());
+ assertNull(entry.getTotalCredentialCount());
+ }
+
+ @Test
+ public void constructor_allParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CreateEntry entry = constructEntryWithAllParams();
+
+ assertNotNull(entry);
+ assertEntryWithAllParams(entry);
+ }
+
+ @Test
+ public void constructor_nullAccountName_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null title to throw NPE",
+ NullPointerException.class,
+ () -> new CreateEntry.Builder(
+ null, mPendingIntent).build());
+ }
+
+ @Test
+ public void constructor_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new CreateEntry.Builder(ACCOUNT_NAME, null).build());
+ }
+
+ @Test
+ public void constructor_emptyAccountName_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty account name to throw NPE",
+ IllegalArgumentException.class,
+ () -> new CreateEntry.Builder("", mPendingIntent).build());
+ }
+
+ @Test
+ public void fromSlice_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CreateEntry originalEntry = constructEntryWithRequiredParams();
+
+ CreateEntry entry = CreateEntry.fromSlice(
+ CreateEntry.toSlice(originalEntry));
+
+ assertNotNull(entry);
+ assertEntryWithRequiredParams(entry);
+ }
+
+ @Test
+ public void fromSlice_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CreateEntry originalEntry = constructEntryWithAllParams();
+
+ CreateEntry entry = CreateEntry.fromSlice(
+ CreateEntry.toSlice(originalEntry));
+
+ assertNotNull(entry);
+ assertEntryWithAllParams(entry);
+ }
+
+ private CreateEntry constructEntryWithRequiredParams() {
+ return new CreateEntry.Builder(ACCOUNT_NAME, mPendingIntent).build();
+ }
+
+ private void assertEntryWithRequiredParams(CreateEntry entry) {
+ assertThat(ACCOUNT_NAME.equals(entry.getAccountName()));
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+
+ private CreateEntry constructEntryWithAllParams() {
+ return new CreateEntry.Builder(
+ ACCOUNT_NAME,
+ mPendingIntent)
+ .setIcon(ICON)
+ .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+ .setPasswordCredentialCount(PASSWORD_COUNT)
+ .setPublicKeyCredentialCount(PUBLIC_KEY_CREDENTIAL_COUNT)
+ .setTotalCredentialCount(TOTAL_COUNT)
+ .build();
+ }
+
+ private void assertEntryWithAllParams(CreateEntry entry) {
+ assertThat(ACCOUNT_NAME).isEqualTo(entry.getAccountName());
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ assertThat(ICON).isEqualTo(entry.getIcon());
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
+ assertThat(PASSWORD_COUNT).isEqualTo(entry.getPasswordCredentialCount());
+ assertThat(PUBLIC_KEY_CREDENTIAL_COUNT).isEqualTo(entry.getPublicKeyCredentialCount());
+ assertThat(TOTAL_COUNT).isEqualTo(entry.getTotalCredentialCount());
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt
new file mode 100644
index 0000000..13417bd
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.CreateEntry
+import androidx.credentials.provider.CreateEntry.Companion.fromSlice
+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
+import java.time.Instant
+import org.junit.Assert
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreateEntryTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(
+ mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ @Test
+ fun constructor_success_autoSelectDefaultFalse() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParams()
+
+ assertNotNull(entry)
+ assertEntryWithRequiredParams(entry)
+ assertNull(entry.icon)
+ assertNull(entry.lastUsedTime)
+ assertNull(entry.getPasswordCredentialCount())
+ assertNull(entry.getPublicKeyCredentialCount())
+ assertNull(entry.getTotalCredentialCount())
+ assertFalse(entry.isAutoSelectAllowed)
+ }
+
+ @Test
+ fun constructor_requiredParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParams()
+
+ assertNotNull(entry)
+ assertEntryWithRequiredParams(entry)
+ assertNull(entry.icon)
+ assertNull(entry.lastUsedTime)
+ assertNull(entry.getPasswordCredentialCount())
+ assertNull(entry.getPublicKeyCredentialCount())
+ assertNull(entry.getTotalCredentialCount())
+ }
+
+ @Test
+ fun constructor_allParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithAllParams()
+
+ assertNotNull(entry)
+ assertEntryWithAllParams(entry)
+ }
+
+ @Test
+ fun constructor_emptyAccountName_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ Assert.assertThrows(
+ "Expected empty account name to throw NPE",
+ IllegalArgumentException::class.java
+ ) {
+ CreateEntry(
+ "", mPendingIntent
+ )
+ }
+ }
+
+ @Test
+ fun fromSlice_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructEntryWithRequiredParams()
+
+ val entry = fromSlice(CreateEntry.toSlice(originalEntry))
+
+ assertNotNull(entry)
+ entry?.let {
+ assertEntryWithRequiredParams(entry)
+ }
+ }
+
+ @Test
+ fun fromSlice_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructEntryWithAllParams()
+
+ val entry = fromSlice(CreateEntry.toSlice(originalEntry))
+
+ assertNotNull(entry)
+ entry?.let {
+ assertEntryWithAllParams(entry)
+ }
+ }
+
+ private fun constructEntryWithRequiredParams(): CreateEntry {
+ return CreateEntry(
+ ACCOUNT_NAME,
+ mPendingIntent
+ )
+ }
+
+ private fun assertEntryWithRequiredParams(entry: CreateEntry) {
+ Truth.assertThat(ACCOUNT_NAME == entry.accountName)
+ Truth.assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ private fun constructEntryWithAllParams(): CreateEntry {
+ return CreateEntry(
+ ACCOUNT_NAME,
+ mPendingIntent,
+ DESCRIPTION,
+ Instant.ofEpochMilli(LAST_USED_TIME),
+ ICON,
+ PASSWORD_COUNT,
+ PUBLIC_KEY_CREDENTIAL_COUNT,
+ TOTAL_COUNT,
+ AUTO_SELECT_BIT
+ )
+ }
+
+ private fun assertEntryWithAllParams(entry: CreateEntry) {
+ Truth.assertThat(ACCOUNT_NAME).isEqualTo(
+ entry.accountName
+ )
+ Truth.assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ Truth.assertThat(ICON).isEqualTo(
+ entry.icon
+ )
+ Truth.assertThat(LAST_USED_TIME).isEqualTo(
+ entry.lastUsedTime?.toEpochMilli()
+ )
+ Truth.assertThat(PASSWORD_COUNT).isEqualTo(
+ entry.getPasswordCredentialCount()
+ )
+ Truth.assertThat(PUBLIC_KEY_CREDENTIAL_COUNT).isEqualTo(
+ entry.getPublicKeyCredentialCount()
+ )
+ Truth.assertThat(TOTAL_COUNT).isEqualTo(
+ entry.getTotalCredentialCount()
+ )
+ Truth.assertThat(AUTO_SELECT_BIT).isTrue()
+ }
+
+ companion object {
+ private val ACCOUNT_NAME: CharSequence = "account_name"
+ private const val DESCRIPTION = "description"
+ private const val PASSWORD_COUNT = 10
+ private const val PUBLIC_KEY_CREDENTIAL_COUNT = 10
+ private const val TOTAL_COUNT = 10
+ private const val AUTO_SELECT_BIT = true
+ private const val LAST_USED_TIME = 10L
+ private val ICON = Icon.createWithBitmap(
+ Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java
new file mode 100644
index 0000000..41bba92
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.R;
+import androidx.credentials.TestUtilsKt;
+import androidx.credentials.provider.BeginGetCredentialOption;
+import androidx.credentials.provider.BeginGetCustomCredentialOption;
+import androidx.credentials.provider.CustomCredentialEntry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class CustomCredentialEntryJavaTest {
+ private static final CharSequence TITLE = "title";
+ private static final CharSequence SUBTITLE = "subtitle";
+
+ private static final String TYPE = "custom_type";
+ private static final CharSequence TYPE_DISPLAY_NAME = "Password";
+ private static final Long LAST_USED_TIME = 10L;
+ private static final Icon ICON = Icon.createWithBitmap(Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888));
+ private static final boolean IS_AUTO_SELECT_ALLOWED = true;
+ private final BeginGetCredentialOption mBeginCredentialOption =
+ new BeginGetCustomCredentialOption(
+ "id", "custom", new Bundle());
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void build_requiredParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CustomCredentialEntry entry = constructEntryWithRequiredParams();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertEntryWithRequiredParams(entry);
+ }
+
+ @Test
+ public void build_allParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CustomCredentialEntry entry = constructEntryWithAllParams();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertEntryWithAllParams(entry);
+ }
+
+ @Test
+ public void build_nullTitle_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null title to throw NPE",
+ NullPointerException.class,
+ () -> new CustomCredentialEntry.Builder(
+ mContext, TYPE, null, mPendingIntent, mBeginCredentialOption
+ ));
+ }
+
+ @Test
+ public void build_nullContext_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null title to throw NPE",
+ NullPointerException.class,
+ () -> new CustomCredentialEntry.Builder(
+ null, TYPE, TITLE, mPendingIntent, mBeginCredentialOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new CustomCredentialEntry.Builder(
+ mContext, TYPE, TITLE, null, mBeginCredentialOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullBeginOption_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null option to throw NPE",
+ NullPointerException.class,
+ () -> new CustomCredentialEntry.Builder(
+ mContext, TYPE, TITLE, mPendingIntent, null
+ ).build());
+ }
+
+ @Test
+ public void build_emptyTitle_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty title to throw IAE",
+ IllegalArgumentException.class,
+ () -> new CustomCredentialEntry.Builder(
+ mContext, TYPE, "", mPendingIntent, mBeginCredentialOption
+ ).build());
+ }
+
+ @Test
+ public void build_emptyType_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty type to throw NPE",
+ IllegalArgumentException.class,
+ () -> new CustomCredentialEntry.Builder(
+ mContext, "", TITLE, mPendingIntent, mBeginCredentialOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CustomCredentialEntry entry = constructEntryWithRequiredParams();
+
+ assertThat(TestUtilsKt.equals(entry.getIcon(),
+ Icon.createWithResource(mContext, R.drawable.ic_other_sign_in))).isTrue();
+ }
+
+ @Test
+ public void fromSlice_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CustomCredentialEntry originalEntry = constructEntryWithRequiredParams();
+
+ CustomCredentialEntry entry = CustomCredentialEntry.fromSlice(
+ originalEntry.getSlice());
+
+ assertNotNull(entry);
+ assertEntryWithRequiredParamsFromSlice(entry);
+ }
+
+ @Test
+ public void fromSlice_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ CustomCredentialEntry originalEntry = constructEntryWithAllParams();
+
+ CustomCredentialEntry entry = CustomCredentialEntry.fromSlice(
+ originalEntry.getSlice());
+
+ assertNotNull(entry);
+ assertEntryWithAllParamsFromSlice(entry);
+ }
+
+ private CustomCredentialEntry constructEntryWithRequiredParams() {
+ return new CustomCredentialEntry.Builder(
+ mContext,
+ TYPE,
+ TITLE,
+ mPendingIntent,
+ mBeginCredentialOption
+ ).build();
+ }
+
+ private CustomCredentialEntry constructEntryWithAllParams() {
+ return new CustomCredentialEntry.Builder(
+ mContext,
+ TYPE,
+ TITLE,
+ mPendingIntent,
+ mBeginCredentialOption)
+ .setIcon(ICON)
+ .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+ .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+ .setTypeDisplayName(TYPE_DISPLAY_NAME)
+ .build();
+ }
+
+ private void assertEntryWithRequiredParams(CustomCredentialEntry entry) {
+ assertThat(TITLE.equals(entry.getTitle()));
+ assertThat(TYPE.equals(entry.getType()));
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+
+ private void assertEntryWithRequiredParamsFromSlice(CustomCredentialEntry entry) {
+ assertThat(TITLE.equals(entry.getTitle()));
+ assertThat(TYPE.equals(entry.getType()));
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+
+ private void assertEntryWithAllParams(CustomCredentialEntry entry) {
+ assertThat(TITLE.equals(entry.getTitle()));
+ assertThat(TYPE.equals(entry.getType()));
+ assertThat(SUBTITLE.equals(entry.getSubtitle()));
+ assertThat(TYPE_DISPLAY_NAME.equals(entry.getTypeDisplayName()));
+ assertThat(ICON).isEqualTo(entry.getIcon());
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed());
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ // TODO: Assert BeginOption
+ }
+
+ private void assertEntryWithAllParamsFromSlice(CustomCredentialEntry entry) {
+ assertThat(TITLE.equals(entry.getTitle()));
+ assertThat(TYPE.equals(entry.getType()));
+ assertThat(SUBTITLE.equals(entry.getSubtitle()));
+ assertThat(TYPE_DISPLAY_NAME.equals(entry.getTypeDisplayName()));
+ assertThat(ICON).isEqualTo(entry.getIcon());
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed());
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ // TODO: Assert BeginOption
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt
new file mode 100644
index 0000000..80a9a2c1
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import androidx.core.os.BuildCompat
+import androidx.credentials.R
+import androidx.credentials.equals
+import androidx.credentials.provider.BeginGetCredentialOption
+import androidx.credentials.provider.BeginGetCustomCredentialOption
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry.Companion.fromSlice
+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 java.time.Instant
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CustomCredentialEntryTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+ @Test
+ fun constructor_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParams()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertEntryWithRequiredParams(entry)
+ }
+
+ @Test
+ fun constructor_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithAllParams()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertEntryWithAllParams(entry)
+ }
+
+ @Test
+ fun constructor_allParameters_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry: CustomCredentialEntry = constructEntryWithAllParams()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertEntryWithAllParams(entry)
+ }
+
+ @Test
+ fun constructor_emptyTitle_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ assertThrows(
+ "Expected empty title to throw NPE",
+ IllegalArgumentException::class.java
+ ) {
+ CustomCredentialEntry(
+ mContext, TITLE, mPendingIntent, BeginGetCustomCredentialOption(
+ "id", "", Bundle.EMPTY
+ )
+ )
+ }
+ }
+
+ @Test
+ fun constructor_emptyType_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ assertThrows(
+ "Expected empty type to throw NPE",
+ IllegalArgumentException::class.java
+ ) {
+ CustomCredentialEntry(
+ mContext, TITLE, mPendingIntent, BeginGetCustomCredentialOption(
+ "id", "", Bundle.EMPTY)
+ )
+ }
+ }
+
+ @Test
+ fun constructor_nullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParams()
+
+ assertThat(
+ equals(
+ entry.icon,
+ Icon.createWithResource(mContext, R.drawable.ic_other_sign_in)
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun fromSlice_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructEntryWithRequiredParams()
+
+ val entry = fromSlice(originalEntry.slice)
+
+ assertNotNull(entry)
+ if (entry != null) {
+ assertEntryWithRequiredParamsFromSlice(entry)
+ }
+ }
+
+ @Test
+ fun fromSlice_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructEntryWithAllParams()
+
+ val entry = fromSlice(originalEntry.slice)
+
+ assertNotNull(entry)
+ if (entry != null) {
+ assertEntryWithAllParamsFromSlice(entry)
+ }
+ }
+
+ private fun constructEntryWithRequiredParams(): CustomCredentialEntry {
+ return CustomCredentialEntry(
+ mContext,
+ TITLE,
+ mPendingIntent,
+ BEGIN_OPTION
+ )
+ }
+
+ private fun constructEntryWithAllParams(): CustomCredentialEntry {
+ return CustomCredentialEntry(
+ mContext,
+ TITLE,
+ mPendingIntent,
+ BEGIN_OPTION,
+ SUBTITLE,
+ TYPE_DISPLAY_NAME,
+ Instant.ofEpochMilli(LAST_USED_TIME),
+ ICON,
+ IS_AUTO_SELECT_ALLOWED
+ )
+ }
+
+ private fun assertEntryWithAllParams(entry: CustomCredentialEntry) {
+ assertThat(TITLE == entry.title)
+ assertThat(TYPE == entry.type)
+ assertThat(SUBTITLE == entry.subtitle)
+ assertThat(TYPE_DISPLAY_NAME == entry.typeDisplayName)
+ assertThat(ICON).isEqualTo(entry.icon)
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.lastUsedTime)
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ private fun assertEntryWithAllParamsFromSlice(entry: CustomCredentialEntry) {
+ assertThat(TITLE == entry.title)
+ assertThat(TYPE == entry.type)
+ assertThat(SUBTITLE == entry.subtitle)
+ assertThat(TYPE_DISPLAY_NAME == entry.typeDisplayName)
+ assertThat(ICON).isEqualTo(entry.icon)
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.lastUsedTime)
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ // TODO: Assert BeginOption
+ }
+
+ private fun assertEntryWithRequiredParams(entry: CustomCredentialEntry) {
+ assertThat(TITLE == entry.title)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ // TODO: Assert BeginOption
+ }
+
+ private fun assertEntryWithRequiredParamsFromSlice(entry: CustomCredentialEntry) {
+ assertThat(TITLE == entry.title)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ // TODO: Assert BeginOption
+ }
+
+ companion object {
+ private val TITLE: CharSequence = "title"
+ private val BEGIN_OPTION: BeginGetCredentialOption = BeginGetCustomCredentialOption(
+ "id", "type", Bundle())
+ private val SUBTITLE: CharSequence = "subtitle"
+ private const val TYPE = "custom_type"
+ private val TYPE_DISPLAY_NAME: CharSequence = "Password"
+ private const val LAST_USED_TIME: Long = 10L
+ private val ICON = Icon.createWithBitmap(
+ Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888
+ )
+ )
+ private const val IS_AUTO_SELECT_ALLOWED = true
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
new file mode 100644
index 0000000..94e2caa
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.PasswordCredential;
+import androidx.credentials.R;
+import androidx.credentials.TestUtilsKt;
+import androidx.credentials.provider.BeginGetPasswordOption;
+import androidx.credentials.provider.PasswordCredentialEntry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class PasswordCredentialEntryJavaTest {
+ private static final CharSequence USERNAME = "title";
+ private static final CharSequence DISPLAYNAME = "subtitle";
+ private static final CharSequence TYPE_DISPLAY_NAME = "Password";
+ private static final Long LAST_USED_TIME = 10L;
+
+ private static final boolean IS_AUTO_SELECT_ALLOWED = true;
+
+ private static final Icon ICON = Icon.createWithBitmap(Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888));
+ private final BeginGetPasswordOption mBeginGetPasswordOption = new BeginGetPasswordOption(
+ new HashSet<>(),
+ Bundle.EMPTY, "id");
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void build_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry entry = constructEntryWithRequiredParamsOnly();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertThat(entry.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertEntryWithRequiredParamsOnly(entry, false);
+ }
+
+ @Test
+ public void build_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry entry = constructEntryWithAllParams();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertThat(entry.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertEntryWithAllParams(entry);
+ }
+
+ @Test
+ public void build_nullContext_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null context to throw NPE",
+ NullPointerException.class,
+ () -> new PasswordCredentialEntry.Builder(
+ null, USERNAME, mPendingIntent, mBeginGetPasswordOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullUsername_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null username to throw NPE",
+ NullPointerException.class,
+ () -> new PasswordCredentialEntry.Builder(
+ mContext, null, mPendingIntent, mBeginGetPasswordOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new PasswordCredentialEntry.Builder(
+ mContext, USERNAME, null, mBeginGetPasswordOption
+ ).build());
+ }
+
+ @Test
+ public void build_nullBeginOption_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null option to throw NPE",
+ NullPointerException.class,
+ () -> new PasswordCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, null
+ ).build());
+ }
+
+ @Test
+ public void build_emptyUsername_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty username to throw IllegalArgumentException",
+ IllegalArgumentException.class,
+ () -> new PasswordCredentialEntry.Builder(
+ mContext, "", mPendingIntent, mBeginGetPasswordOption).build());
+ }
+
+ @Test
+ public void build_nullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry entry = new PasswordCredentialEntry
+ .Builder(mContext, USERNAME, mPendingIntent, mBeginGetPasswordOption).build();
+
+ assertThat(TestUtilsKt.equals(entry.getIcon(),
+ Icon.createWithResource(mContext, R.drawable.ic_password))).isTrue();
+ }
+
+ @Test
+ public void build_nullTypeDisplayName_defaultDisplayNameSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, mBeginGetPasswordOption).build();
+
+ assertThat(entry.getTypeDisplayName()).isEqualTo(
+ mContext.getString(
+ R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL)
+ );
+ }
+
+ @Test
+ public void build_isAutoSelectAllowedDefault_false() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry entry = constructEntryWithRequiredParamsOnly();
+
+ assertFalse(entry.isAutoSelectAllowed());
+ }
+
+ @Test
+ public void fromSlice_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry originalEntry = constructEntryWithRequiredParamsOnly();
+
+ assertNotNull(originalEntry.getSlice());
+ PasswordCredentialEntry entry = PasswordCredentialEntry.fromSlice(
+ originalEntry.getSlice());
+
+ assertNotNull(entry);
+ assertEntryWithRequiredParamsOnly(entry, true);
+ }
+
+ @Test
+ public void fromSlice_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PasswordCredentialEntry originalEntry = constructEntryWithAllParams();
+
+ assertNotNull(originalEntry.getSlice());
+ PasswordCredentialEntry entry = PasswordCredentialEntry.fromSlice(
+ originalEntry.getSlice());
+
+ assertNotNull(entry);
+ assertEntryWithAllParams(entry);
+ }
+
+ private PasswordCredentialEntry constructEntryWithRequiredParamsOnly() {
+ return new PasswordCredentialEntry.Builder(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ mBeginGetPasswordOption).build();
+ }
+
+ private PasswordCredentialEntry constructEntryWithAllParams() {
+ return new PasswordCredentialEntry.Builder(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ mBeginGetPasswordOption)
+ .setDisplayName(DISPLAYNAME)
+ .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+ .setIcon(ICON)
+ .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+ .build();
+ }
+
+ private void assertEntryWithRequiredParamsOnly(PasswordCredentialEntry entry,
+ Boolean assertOptionIdOnly) {
+ assertThat(USERNAME.equals(entry.getUsername()));
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ // TODO: Assert BeginOption
+ }
+
+ private void assertEntryWithAllParams(PasswordCredentialEntry entry) {
+ assertThat(USERNAME.equals(entry.getUsername()));
+ assertThat(DISPLAYNAME.equals(entry.getDisplayName()));
+ assertThat(TYPE_DISPLAY_NAME.equals(entry.getTypeDisplayName()));
+ assertThat(ICON).isEqualTo(entry.getIcon());
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed());
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ // TODO: Assert BeginOption
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
new file mode 100644
index 0000000..6c86911
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import androidx.core.os.BuildCompat
+import androidx.credentials.PasswordCredential
+import androidx.credentials.R
+import androidx.credentials.equals
+import androidx.credentials.provider.BeginGetPasswordOption
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry.Companion.fromSlice
+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 java.time.Instant
+import junit.framework.TestCase.assertFalse
+import junit.framework.TestCase.assertNotNull
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PasswordCredentialEntryTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(
+ mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ @Test
+ fun constructor_requiredParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParamsOnly()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertThat(entry.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ assertEntryWithRequiredParamsOnly(entry)
+ }
+
+ @Test
+ fun constructor_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithAllParams()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertThat(entry.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ assertEntryWithAllParams(entry)
+ }
+
+ @Test
+ fun constructor_emptyUsername_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ assertThrows(
+ "Expected empty username to throw IllegalArgumentException",
+ IllegalArgumentException::class.java
+ ) {
+ PasswordCredentialEntry(
+ mContext, "", mPendingIntent, BEGIN_OPTION
+ )
+ }
+ }
+
+ @Test
+ fun constructor_nullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = PasswordCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, BEGIN_OPTION).build()
+
+ assertThat(
+ equals(
+ entry.icon,
+ Icon.createWithResource(mContext, R.drawable.ic_password)
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun constructor_nullTypeDisplayName_defaultDisplayNameSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = PasswordCredentialEntry(
+ mContext, USERNAME, mPendingIntent, BEGIN_OPTION)
+
+ assertThat(entry.typeDisplayName).isEqualTo(
+ mContext.getString(
+ R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL
+ )
+ )
+ }
+
+ @Test
+ fun constructor_isAutoSelectAllowedDefault_false() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructEntryWithRequiredParamsOnly()
+ val entry1 = constructEntryWithAllParams()
+
+ assertFalse(entry.isAutoSelectAllowed)
+ assertFalse(entry1.isAutoSelectAllowed)
+ }
+
+ @Test
+ fun fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructEntryWithAllParams()
+
+ val entry = fromSlice(originalEntry.slice)
+
+ assertNotNull(entry)
+ entry?.let {
+ assertEntryWithAllParams(entry)
+ }
+ }
+
+ private fun constructEntryWithRequiredParamsOnly(): PasswordCredentialEntry {
+ return PasswordCredentialEntry(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ BEGIN_OPTION
+ )
+ }
+
+ private fun constructEntryWithAllParams(): PasswordCredentialEntry {
+ return PasswordCredentialEntry(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ BEGIN_OPTION,
+ DISPLAYNAME,
+ LAST_USED_TIME,
+ ICON
+ )
+ }
+
+ private fun assertEntryWithRequiredParamsOnly(entry: PasswordCredentialEntry) {
+ assertThat(USERNAME == entry.username)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ private fun assertEntryWithAllParams(entry: PasswordCredentialEntry) {
+ assertThat(USERNAME == entry.username)
+ assertThat(DISPLAYNAME == entry.displayName)
+ assertThat(TYPE_DISPLAY_NAME == entry.typeDisplayName)
+ assertThat(ICON).isEqualTo(entry.icon)
+ assertNotNull(entry.lastUsedTime)
+ entry.lastUsedTime?.let {
+ assertThat(LAST_USED_TIME.toEpochMilli()).isEqualTo(
+ it.toEpochMilli())
+ }
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ companion object {
+ private val USERNAME: CharSequence = "title"
+ private val DISPLAYNAME: CharSequence = "subtitle"
+ private val TYPE_DISPLAY_NAME: CharSequence = "Password"
+ private val LAST_USED_TIME = Instant.now()
+ private val BEGIN_OPTION = BeginGetPasswordOption(
+ emptySet<String>(),
+ Bundle.EMPTY, "id")
+ private val ICON = Icon.createWithBitmap(
+ Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java
new file mode 100644
index 0000000..aefc55a
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.PublicKeyCredential;
+import androidx.credentials.R;
+import androidx.credentials.TestUtilsKt;
+import androidx.credentials.provider.BeginGetPublicKeyCredentialOption;
+import androidx.credentials.provider.PublicKeyCredentialEntry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class PublicKeyCredentialEntryJavaTest {
+ private static final CharSequence USERNAME = "title";
+ private static final CharSequence DISPLAYNAME = "subtitle";
+ private static final CharSequence TYPE_DISPLAY_NAME = "Password";
+ private static final Long LAST_USED_TIME = 10L;
+ private static final Icon ICON = Icon.createWithBitmap(Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888));
+ private static final boolean IS_AUTO_SELECT_ALLOWED = true;
+ private final BeginGetPublicKeyCredentialOption mBeginOption =
+ new BeginGetPublicKeyCredentialOption(
+ new Bundle(), "id", "json");
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void build_requiredParamsOnly_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PublicKeyCredentialEntry entry = constructWithRequiredParamsOnly();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertThat(entry.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertEntryWithRequiredParams(entry);
+ }
+
+ @Test
+ public void build_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PublicKeyCredentialEntry entry = constructWithAllParams();
+
+ assertNotNull(entry);
+ assertNotNull(entry.getSlice());
+ assertThat(entry.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertEntryWithAllParams(entry);
+ }
+
+ @Test
+ public void build_withNullUsername_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null username to throw NPE",
+ NullPointerException.class,
+ () -> new PublicKeyCredentialEntry.Builder(
+ mContext, null, mPendingIntent, mBeginOption
+ ).build());
+ }
+
+ @Test
+ public void build_withNullBeginOption_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null option to throw NPE",
+ NullPointerException.class,
+ () -> new PublicKeyCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, null
+ ).build());
+ }
+
+ @Test
+ public void build_withNullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new PublicKeyCredentialEntry.Builder(
+ mContext, USERNAME, null, mBeginOption
+ ).build());
+ }
+
+ @Test
+ public void build_withEmptyUsername_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected empty username to throw IllegalArgumentException",
+ IllegalArgumentException.class,
+ () -> new PublicKeyCredentialEntry.Builder(
+ mContext, "", mPendingIntent, mBeginOption).build());
+ }
+
+ @Test
+ public void build_withNullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PublicKeyCredentialEntry entry = new PublicKeyCredentialEntry
+ .Builder(
+ mContext, USERNAME, mPendingIntent, mBeginOption).build();
+
+ assertThat(TestUtilsKt.equals(entry.getIcon(),
+ Icon.createWithResource(mContext, R.drawable.ic_passkey))).isTrue();
+ }
+
+ @Test
+ public void build_nullTypeDisplayName_defaultDisplayNameSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PublicKeyCredentialEntry entry = new PublicKeyCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, mBeginOption).build();
+
+ assertThat(entry.getTypeDisplayName()).isEqualTo(
+ mContext.getString(
+ R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL)
+ );
+ }
+
+ @Test
+ public void fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ PublicKeyCredentialEntry originalEntry = constructWithAllParams();
+ assertNotNull(originalEntry.getSlice());
+
+ PublicKeyCredentialEntry entry = PublicKeyCredentialEntry.fromSlice(
+ originalEntry.getSlice());
+
+ assertNotNull(entry);
+ assertEntryWithRequiredParams(entry);
+ }
+
+ private PublicKeyCredentialEntry constructWithRequiredParamsOnly() {
+ return new PublicKeyCredentialEntry.Builder(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ mBeginOption
+ ).build();
+ }
+
+ private PublicKeyCredentialEntry constructWithAllParams() {
+ return new PublicKeyCredentialEntry.Builder(
+ mContext, USERNAME, mPendingIntent, mBeginOption)
+ .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+ .setDisplayName(DISPLAYNAME)
+ .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+ .setIcon(ICON)
+ .build();
+ }
+
+ private void assertEntryWithRequiredParams(PublicKeyCredentialEntry entry) {
+ assertThat(USERNAME.equals(entry.getUsername()));
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+
+ private void assertEntryWithAllParams(PublicKeyCredentialEntry entry) {
+ assertThat(USERNAME.equals(entry.getUsername()));
+ assertThat(DISPLAYNAME.equals(entry.getDisplayName()));
+ assertThat(TYPE_DISPLAY_NAME.equals(entry.getTypeDisplayName()));
+ assertThat(ICON).isEqualTo(entry.getIcon());
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.getLastUsedTime());
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed());
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt
new file mode 100644
index 0000000..93c9eba
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import androidx.core.os.BuildCompat
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.R
+import androidx.credentials.equals
+import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry.Companion.fromSlice
+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 java.time.Instant
+import junit.framework.TestCase.assertNotNull
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PublicKeyCredentialEntryTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(
+ mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+ @Test
+ fun constructor_requiredParamsOnly_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructWithRequiredParamsOnly()
+
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertThat(entry.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertEntryWithRequiredParams(entry)
+ }
+
+ @Test
+ fun constructor_allParams_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = constructWithAllParams()
+ assertNotNull(entry)
+ assertNotNull(entry.slice)
+ assertThat(entry.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertEntryWithAllParams(entry)
+ }
+
+ @Test
+ fun constructor_emptyUsername_throwsIAE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ assertThrows(
+ "Expected empty username to throw IllegalArgumentException",
+ IllegalArgumentException::class.java
+ ) {
+ PublicKeyCredentialEntry(
+ mContext, "", mPendingIntent, BEGIN_OPTION
+ )
+ }
+ }
+
+ @Test
+ fun constructor_nullIcon_defaultIconSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = PublicKeyCredentialEntry(
+ mContext, USERNAME, mPendingIntent, BEGIN_OPTION
+ )
+
+ assertThat(
+ equals(
+ entry.icon,
+ Icon.createWithResource(mContext, R.drawable.ic_passkey)
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun constructor_nullTypeDisplayName_defaultDisplayNameSet() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = PublicKeyCredentialEntry(
+ mContext, USERNAME, mPendingIntent, BEGIN_OPTION
+ )
+ assertThat(entry.typeDisplayName).isEqualTo(
+ mContext.getString(
+ R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL
+ )
+ )
+ }
+
+ @Test
+ fun fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = constructWithAllParams()
+
+ val entry = fromSlice(originalEntry.slice)
+
+ assertNotNull(entry)
+ entry?.let {
+ assertEntryWithRequiredParams(entry)
+ }
+ }
+
+ private fun constructWithRequiredParamsOnly(): PublicKeyCredentialEntry {
+ return PublicKeyCredentialEntry(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ BEGIN_OPTION
+ )
+ }
+
+ private fun constructWithAllParams(): PublicKeyCredentialEntry {
+ return PublicKeyCredentialEntry(
+ mContext,
+ USERNAME,
+ mPendingIntent,
+ BEGIN_OPTION,
+ DISPLAYNAME,
+ Instant.ofEpochMilli(LAST_USED_TIME),
+ ICON,
+ IS_AUTO_SELECT_ALLOWED
+ )
+ }
+
+ private fun assertEntryWithRequiredParams(entry: PublicKeyCredentialEntry) {
+ assertThat(USERNAME == entry.username)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ private fun assertEntryWithAllParams(entry: PublicKeyCredentialEntry) {
+ assertThat(USERNAME == entry.username)
+ assertThat(DISPLAYNAME == entry.displayName)
+ assertThat(TYPE_DISPLAY_NAME == entry.typeDisplayName)
+ assertThat(ICON).isEqualTo(entry.icon)
+ assertThat(Instant.ofEpochMilli(LAST_USED_TIME)).isEqualTo(entry.lastUsedTime)
+ assertThat(IS_AUTO_SELECT_ALLOWED).isEqualTo(entry.isAutoSelectAllowed)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ companion object {
+ private val BEGIN_OPTION: BeginGetPublicKeyCredentialOption =
+ BeginGetPublicKeyCredentialOption(Bundle(), "id", "json")
+ private val USERNAME: CharSequence = "title"
+ private val DISPLAYNAME: CharSequence = "subtitle"
+ private val TYPE_DISPLAY_NAME: CharSequence = "Password"
+ private const val LAST_USED_TIME: Long = 10L
+ private val ICON = Icon.createWithBitmap(Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888))
+ private const val IS_AUTO_SELECT_ALLOWED = true
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java
new file mode 100644
index 0000000..6c45580
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.core.os.BuildCompat;
+import androidx.credentials.provider.RemoteEntry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+public class RemoteEntryJavaTest {
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final Intent mIntent = new Intent();
+ private final PendingIntent mPendingIntent =
+ PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ @Test
+ public void constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ RemoteEntry entry = new RemoteEntry(mPendingIntent);
+
+ assertNotNull(entry);
+ assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+ }
+
+ @Test
+ public void constructor_nullPendingIntent_throwsNPE() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ assertThrows("Expected null pending intent to throw NPE",
+ NullPointerException.class,
+ () -> new RemoteEntry(null));
+ }
+
+ @Test
+ public void fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return;
+ }
+ RemoteEntry originalEntry = new RemoteEntry(mPendingIntent);
+
+ RemoteEntry fromSlice = RemoteEntry.fromSlice(RemoteEntry.toSlice(originalEntry));
+
+ assertThat(fromSlice).isNotNull();
+ assertThat(fromSlice.getPendingIntent()).isEqualTo(mPendingIntent);
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryTest.kt
new file mode 100644
index 0000000..fd54771
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.RemoteEntry
+import androidx.credentials.provider.RemoteEntry.Companion.fromSlice
+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 junit.framework.TestCase.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+class RemoteEntryTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mIntent = Intent()
+ private val mPendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+
+ @Test
+ fun constructor_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val entry = RemoteEntry(mPendingIntent)
+
+ assertNotNull(entry)
+ assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+ }
+
+ @Test
+ fun fromSlice_success() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val originalEntry = RemoteEntry(mPendingIntent)
+
+ val fromSlice = fromSlice(RemoteEntry.toSlice(originalEntry))
+
+ assertThat(fromSlice).isNotNull()
+ if (fromSlice != null) {
+ assertThat(fromSlice.pendingIntent).isEqualTo(mPendingIntent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt
new file mode 100644
index 0000000..60cd479
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/UiUtils.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.ui
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.BeginGetPasswordOption
+import androidx.credentials.provider.CreateEntry
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+
+@SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
+class UiUtils {
+ companion object {
+ private val sContext = ApplicationProvider.getApplicationContext<Context>()
+ private val sIntent = Intent()
+ private val sPendingIntent = PendingIntent.getActivity(
+ sContext, 0, sIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ private val ACCOUNT_NAME: CharSequence = "account_name"
+ private const val DESCRIPTION = "description"
+ private const val PASSWORD_COUNT = 10
+ private const val PUBLIC_KEY_CREDENTIAL_COUNT = 10
+ private const val TOTAL_COUNT = 10
+ private const val LAST_USED_TIME = 10L
+ private val ICON = Icon.createWithBitmap(
+ Bitmap.createBitmap(
+ 100, 100, Bitmap.Config.ARGB_8888
+ )
+ )
+ private val BEGIN_OPTION = BeginGetPasswordOption(
+ setOf(), Bundle.EMPTY, "id"
+ )
+
+ /**
+ * Generates a default authentication action entry that can be used for tests around the
+ * provider objects.
+ */
+ @JvmStatic
+ fun constructAuthenticationActionEntry(title: CharSequence): AuthenticationAction {
+ return AuthenticationAction(title, sPendingIntent)
+ }
+
+ /**
+ * Generates a default action entry that can be used for tests around the provider
+ * objects.
+ */
+ @JvmStatic
+ fun constructActionEntry(title: CharSequence, subtitle: CharSequence): Action {
+ return Action(title, sPendingIntent, subtitle)
+ }
+
+ /**
+ * Generates a default password credential entry that can be used for tests around the
+ * provider objects.
+ */
+ @JvmStatic
+ fun constructPasswordCredentialEntryDefault(username: CharSequence): CredentialEntry {
+ return PasswordCredentialEntry(
+ sContext,
+ username,
+ sPendingIntent,
+ BEGIN_OPTION
+ )
+ }
+
+ /**
+ * Generate a default remote entry that can be used for tests around the provider objects.
+ */
+ @JvmStatic
+ fun constructRemoteEntryDefault(): RemoteEntry {
+ return RemoteEntry(sPendingIntent)
+ }
+
+ /**
+ * Generates a create entry with known inputs for accountName and description in order
+ * to test proper formation.
+ *
+ * @param accountName the account name associated with the create entry
+ * @param description the description associated with the create entry
+ */
+ @JvmStatic
+ fun constructCreateEntryWithSimpleParams(
+ accountName: CharSequence,
+ description: CharSequence
+ ):
+ CreateEntry {
+ return CreateEntry.Builder(accountName, sPendingIntent).setDescription(description)
+ .build()
+ }
+
+ @JvmStatic
+ fun constructRemoteEntry():
+ RemoteEntry {
+ return RemoteEntry(sPendingIntent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
index 10c6a67..937d93e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
@@ -20,7 +20,6 @@
import android.os.Bundle
import android.text.TextUtils
import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.credentials.PublicKeyCredential.Companion.BUNDLE_KEY_SUBTYPE
import androidx.credentials.internal.FrameworkClassParsingException
@@ -31,33 +30,46 @@
* An application can construct a subtype request and call [CredentialManager.createCredential] to
* launch framework UI flows to collect consent and any other metadata needed from the user to
* register a new user credential.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass (e.g.
+ * the type for [CreatePasswordRequest] is [PasswordCredential.TYPE_PASSWORD_CREDENTIAL] and for
+ * [CreatePublicKeyCredentialRequest] is [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL])
+ * @property credentialData the request data in the [Bundle] format
+ * @property candidateQueryData the partial request data in the [Bundle] format that will be sent
+ * to the provider during the initial candidate query stage, which should not contain sensitive
+ * user credential information (note: bundle keys in the form of `androidx.credentials.*` are
+ * reserved for internal library use)
+ * @property isSystemProviderRequired true if must only be fulfilled by a system provider and
+ * false otherwise
+ * @property isAutoSelectAllowed whether a create option will be automatically chosen if it is
+ * the only one available to the user
+ * @property displayInfo the information to be displayed on the screen
+ * @property origin the origin of a different application if the request is being made on behalf of
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available passkey registration offering instead of falling back to
+ * discovering remote options, and false (preferred by default) otherwise
*/
abstract class CreateCredentialRequest internal constructor(
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val type: String,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val credentialData: Bundle,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val candidateQueryData: Bundle,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val isSystemProviderRequired: Boolean,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val isAutoSelectAllowed: Boolean,
- /** @hide */
+ val type: String,
+ val credentialData: Bundle,
+ val candidateQueryData: Bundle,
+ val isSystemProviderRequired: Boolean,
+ val isAutoSelectAllowed: Boolean,
val displayInfo: DisplayInfo,
val origin: String?,
+ @get:JvmName("preferImmediatelyAvailableCredentials")
+ val preferImmediatelyAvailableCredentials: Boolean,
) {
init {
- @Suppress("UNNECESSARY_SAFE_CALL")
- credentialData?.let {
- credentialData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
- }
+ credentialData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
+ credentialData.putBoolean(
+ BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ preferImmediatelyAvailableCredentials
+ )
+ candidateQueryData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
}
/**
@@ -75,7 +87,7 @@
/** @hide */
val credentialTypeIcon: Icon?,
/** @hide */
- val defaultProvider: String?,
+ val preferDefaultProvider: String?,
) {
/**
@@ -97,6 +109,31 @@
null,
)
+ /**
+ * Constructs a [DisplayInfo].
+ *
+ * @param userId the user id of the created credential
+ * @param userDisplayName an optional display name in addition to the [userId] that may be
+ * displayed next to the `userId` during the user consent to help your user better
+ * understand the credential being created
+ * @param preferDefaultProvider the preferred default provider component name to prioritize in the
+ * selection UI flows. Your app must have the permission
+ * android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it
+ * would not take effect. Also this bit may not take effect for Android API level 33 and
+ * below, depending on the pre-34 provider(s) you have chosen.
+ * @throws IllegalArgumentException If [userId] is empty
+ */
+ constructor(
+ userId: CharSequence,
+ userDisplayName: CharSequence?,
+ preferDefaultProvider: String?
+ ) : this(
+ userId,
+ userDisplayName,
+ null,
+ preferDefaultProvider,
+ )
+
init {
require(userId.isNotEmpty()) { "userId should not be empty" }
}
@@ -109,8 +146,8 @@
if (!TextUtils.isEmpty(userDisplayName)) {
bundle.putCharSequence(BUNDLE_KEY_USER_DISPLAY_NAME, userDisplayName)
}
- if (!TextUtils.isEmpty(defaultProvider)) {
- bundle.putString(BUNDLE_KEY_DEFAULT_PROVIDER, defaultProvider)
+ if (!TextUtils.isEmpty(preferDefaultProvider)) {
+ bundle.putString(BUNDLE_KEY_DEFAULT_PROVIDER, preferDefaultProvider)
}
// Today the type icon is determined solely within this library right before the
// request is passed into the framework. Later if needed a new API can be added for
@@ -171,6 +208,8 @@
/** @hide */
companion object {
+ internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
+ "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
/** @hide */
const val BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED =
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
@@ -218,7 +257,10 @@
DisplayInfo.parseFromCredentialDataBundle(
credentialData
) ?: return null,
- credentialData.getBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, false)
+ credentialData.getBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, false),
+ origin,
+ credentialData.getBoolean(
+ BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS, false),
)
}
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
index 92bc244..3f60032 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
@@ -17,20 +17,20 @@
package androidx.credentials
import android.os.Bundle
-import androidx.annotation.RestrictTo
import androidx.credentials.internal.FrameworkClassParsingException
/**
* Base response class for the credential creation operation made with the
* [CreateCredentialRequest].
+ *
+ * @property type the credential type determined by the credential-type-specific subclass (e.g.
+ * the type for [CreatePasswordResponse] is [PasswordCredential.TYPE_PASSWORD_CREDENTIAL] and for
+ * [CreatePublicKeyCredentialResponse] is [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL])
+ * @property data the response data in the [Bundle] format
*/
abstract class CreateCredentialResponse internal constructor(
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val type: String,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val data: Bundle,
+ val type: String,
+ val data: Bundle,
) {
/** @hide */
companion object {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialRequest.kt
index 09c6b6f..b5f580e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialRequest.kt
@@ -36,32 +36,36 @@
* @param type the credential type determined by the credential-type-specific subclass for
* custom use cases
* @param credentialData the data of this [CreateCustomCredentialRequest] in the [Bundle]
- * format (note: bundle keys in the form of `androidx.credentials.*` are reserved for internal
- * library use)
+ * format (note: bundle keys in the form of `androidx.credentials.*` and `android.credentials.*` are
+ * reserved for internal library usage)
* @param candidateQueryData the partial request data in the [Bundle] format that will be sent
* to the provider during the initial candidate query stage, which should not contain sensitive
- * user credential information (note: bundle keys in the form of `androidx.credentials.*` are
- * reserved for internal library use)
+ * user credential information (note: bundle keys in the form of `androidx.credentials.*` and
+ * `android.credentials.*` are reserved for internal library usage)
* @param isSystemProviderRequired true if must only be fulfilled by a system provider and
* false otherwise
* @param isAutoSelectAllowed defines if a create entry will be automatically chosen if it is
* the only one available option, false by default
* @param displayInfo the information to be displayed on the screen
* @param origin the origin of a different application if the request is being made on behalf of
- * that application. For API level >=34, setting a non-null value for this parameter, will throw
- * a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present.
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available passkey registration offering instead of falling back to
+ * discovering remote options, and false (default) otherwise
* @throws IllegalArgumentException If [type] is empty
* @throws NullPointerException If [type], [credentialData], or [candidateQueryData] is null
*/
open class CreateCustomCredentialRequest
@JvmOverloads constructor(
- final override val type: String,
- final override val credentialData: Bundle,
- final override val candidateQueryData: Bundle,
- final override val isSystemProviderRequired: Boolean,
+ type: String,
+ credentialData: Bundle,
+ candidateQueryData: Bundle,
+ isSystemProviderRequired: Boolean,
displayInfo: DisplayInfo,
- final override val isAutoSelectAllowed: Boolean = false,
+ isAutoSelectAllowed: Boolean = false,
origin: String? = null,
+ preferImmediatelyAvailableCredentials: Boolean = false,
) : CreateCredentialRequest(
type,
credentialData,
@@ -69,9 +73,9 @@
isSystemProviderRequired,
isAutoSelectAllowed,
displayInfo,
- origin
+ origin,
+ preferImmediatelyAvailableCredentials
) {
-
init {
require(type.isNotEmpty()) { "type should not be empty" }
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialResponse.kt
index caf0dac..a623a2e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCustomCredentialResponse.kt
@@ -30,15 +30,17 @@
* Note: The Bundle keys for [data] should not be in the form of androidx.credentials.*` as they
* are reserved for internal use by this androidx library.
*
- * @property type the credential type determined by the credential-type-specific subclass for custom
+ * @param type the credential type determined by the credential-type-specific subclass for custom
* use cases
- * @property data the response data in the [Bundle] format for custom use cases
+ * @param data the response data in the [Bundle] format for custom use cases (note: bundle keys in
+ * the form of `androidx.credentials.*` and `android.credentials.*` are reserved for internal
+ * library usage)
* @throws IllegalArgumentException If [type] is empty
* @throws NullPointerException If [type] or [data] are null
*/
open class CreateCustomCredentialResponse(
- final override val type: String,
- final override val data: Bundle
+ type: String,
+ data: Bundle
) : CreateCredentialResponse(type, data) {
init {
require(type.isNotEmpty()) { "type should not be empty" }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
index 62b970b..de720a5 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
@@ -26,16 +26,21 @@
*
* @property id the user id associated with the password
* @property password the password
+ * @param id the user id associated with the password
+ * @param password the password
* @param origin the origin of a different application if the request is being made on behalf of
- * that application. For API level >=34, setting a non-null value for this parameter, will throw a
- * SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present when
- * API level >= 34.
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available credential creation offering instead of falling back to
+ * discovering remote options, and false (default) otherwise
*/
class CreatePasswordRequest private constructor(
val id: String,
val password: String,
displayInfo: DisplayInfo,
origin: String? = null,
+ preferImmediatelyAvailableCredentials: Boolean,
) : CreateCredentialRequest(
type = PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
credentialData = toCredentialDataBundle(id, password),
@@ -43,7 +48,8 @@
isSystemProviderRequired = false,
isAutoSelectAllowed = false,
displayInfo,
- origin
+ origin,
+ preferImmediatelyAvailableCredentials,
) {
/**
@@ -53,15 +59,60 @@
* @param id the user id associated with the password
* @param password the password
* @param origin the origin of a different application if the request is being made on behalf of
- * that application. For API level >=34, setting a non-null value for this parameter, will throw
- * a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present.
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available password saving option instead of falling back
+ * to discovering remote options, and false (default) otherwise
* @throws NullPointerException If [id] is null
* @throws NullPointerException If [password] is null
* @throws IllegalArgumentException If [password] is empty
- * @throws SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present
+ * @throws SecurityException if [origin] is set but
+ * android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present
*/
- @JvmOverloads constructor(id: String, password: String, origin: String? = null) : this(id,
- password, DisplayInfo(id, null), origin)
+ @JvmOverloads constructor(
+ id: String,
+ password: String,
+ origin: String? = null,
+ preferImmediatelyAvailableCredentials: Boolean = false,
+ ) : this(id, password, DisplayInfo(id, null), origin, preferImmediatelyAvailableCredentials)
+
+ /**
+ * Constructs a [CreatePasswordRequest] to save the user password credential with their
+ * password provider.
+ *
+ * @param id the user id associated with the password
+ * @param password the password
+ * @param origin the origin of a different application if the request is being made on behalf of
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferDefaultProvider the preferred default provider component name to prioritize in
+ * the selection UI flows (Note: your app must have the permission
+ * android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it
+ * would not take effect; also this bit may not take effect for Android API level 33 and below,
+ * depending on the pre-34 provider(s) you have chosen)
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available passkey registration offering instead of falling back
+ * to discovering remote options, and false (preferably) otherwise
+ * @throws NullPointerException If [id] is null
+ * @throws NullPointerException If [password] is null
+ * @throws IllegalArgumentException If [password] is empty
+ * @throws SecurityException if [origin] is set but
+ * android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present
+ */
+ constructor(
+ id: String,
+ password: String,
+ origin: String?,
+ preferDefaultProvider: String?,
+ preferImmediatelyAvailableCredentials: Boolean,
+ ) : this(
+ id, password, DisplayInfo(
+ userId = id,
+ userDisplayName = null,
+ preferDefaultProvider = preferDefaultProvider,
+ ), origin, preferImmediatelyAvailableCredentials,
+ )
init {
require(password.isNotEmpty()) { "password should not be empty" }
@@ -89,16 +140,25 @@
@JvmStatic
@RequiresApi(23)
- internal fun createFrom(data: Bundle, origin: String? = null): CreatePasswordRequest {
+ internal fun createFrom(data: Bundle, origin: String?): CreatePasswordRequest {
try {
val id = data.getString(BUNDLE_KEY_ID)
val password = data.getString(BUNDLE_KEY_PASSWORD)
val displayInfo = DisplayInfo.parseFromCredentialDataBundle(data)
+ val preferImmediatelyAvailableCredentials =
+ data.getBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS, false)
return if (displayInfo == null) CreatePasswordRequest(
- id!!,
- password!!,
- origin
- ) else CreatePasswordRequest(id!!, password!!, displayInfo, origin)
+ id = id!!,
+ password = password!!,
+ origin = origin,
+ preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
+ ) else CreatePasswordRequest(
+ id = id!!,
+ password = password!!,
+ displayInfo = displayInfo,
+ origin = origin,
+ preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
+ )
} catch (e: Exception) {
throw FrameworkClassParsingException()
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
index 366d9f1..b81ae9d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
@@ -25,37 +25,40 @@
/**
* A request to register a passkey from the user's public key credential provider.
*
+ * @property requestJson the request in JSON format in the [standard webauthn web json](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson).
+ * @property clientDataHash a clientDataHash value to sign over in place of assembling and hashing
+ * clientDataJSON during the signature request; only meaningful when [origin] is set
* @param requestJson the request in JSON format in the [standard webauthn web json](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson).
- * @param clientDataHash a hash that is used to verify the origin
+ * @param clientDataHash a clientDataHash value to sign over in place of assembling and hashing
+ * clientDataJSON during the signature request; only meaningful when [origin] is set
* @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
* immediately when there is no available passkey registration offering instead of falling back to
* discovering remote options, and false (default) otherwise
* @param origin the origin of a different application if the request is being made on behalf of
- * that application. For API level >=34, setting a non-null value for this parameter, will throw
- * a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present.
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
*/
class CreatePublicKeyCredentialRequest private constructor(
val requestJson: String,
- val clientDataHash: String?,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean,
+ val clientDataHash: ByteArray?,
+ preferImmediatelyAvailableCredentials: Boolean,
displayInfo: DisplayInfo,
origin: String? = null,
) : CreateCredentialRequest(
type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- credentialData = toCredentialDataBundle(requestJson, clientDataHash,
- preferImmediatelyAvailableCredentials),
+ credentialData = toCredentialDataBundle(requestJson, clientDataHash),
// The whole request data should be passed during the query phase.
- candidateQueryData = toCredentialDataBundle(requestJson, clientDataHash,
- preferImmediatelyAvailableCredentials),
+ candidateQueryData = toCandidateDataBundle(requestJson, clientDataHash),
isSystemProviderRequired = false,
isAutoSelectAllowed = false,
displayInfo,
- origin
+ origin,
+ preferImmediatelyAvailableCredentials
) {
/**
- * Constructs a [CreatePublicKeyCredentialRequest] to register a passkey from the user's public key credential provider.
+ * Constructs a [CreatePublicKeyCredentialRequest] to register a passkey from the user's public
+ * key credential provider.
*
* @param requestJson the privileged request in JSON format in the [standard webauthn web json](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson).
* @param clientDataHash a hash that is used to verify the relying party identity
@@ -63,28 +66,58 @@
* immediately when there is no available passkey registration offering instead of falling back to
* discovering remote options, and false (default) otherwise
* @param origin the origin of a different application if the request is being made on behalf of
- * that application. For API level >=34, setting a non-null value for this parameter, will throw
- * a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present.
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
* @throws NullPointerException If [requestJson] is null
* @throws IllegalArgumentException If [requestJson] is empty, or if it doesn't have a valid
* `user.name` defined according to the [webauthn spec](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson)
*/
@JvmOverloads constructor(
requestJson: String,
- clientDataHash: String? = null,
+ clientDataHash: ByteArray? = null,
preferImmediatelyAvailableCredentials: Boolean = false,
origin: String? = null
) : this(requestJson, clientDataHash, preferImmediatelyAvailableCredentials,
getRequestDisplayInfo(requestJson), origin)
+ /**
+ * Constructs a [CreatePublicKeyCredentialRequest] to register a passkey from the user's public
+ * key credential provider.
+ *
+ * @param requestJson the privileged request in JSON format in the [standard webauthn web
+ * json](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson).
+ * @param clientDataHash a hash that is used to verify the relying party identity
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available passkey registration offering instead of falling back to
+ * discovering remote options, and false (preferably) otherwise
+ * @param origin the origin of a different application if the request is being made on behalf of
+ * that application (Note: for API level >=34, setting a non-null value for this parameter will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferDefaultProvider the preferred default provider component name to prioritize in
+ * the selection UI flows (Note: tour app must have the permission
+ * android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or it
+ * would not take effect; also this bit may not take effect for Android API level 33 and below,
+ * depending on the pre-34 provider(s) you have chosen)
+ * @throws NullPointerException If [requestJson] is null
+ * @throws IllegalArgumentException If [requestJson] is empty, or if it doesn't have a valid
+ * `user.name` defined according to the [webauthn
+ * spec](https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptionsjson)
+ */
+ constructor(
+ requestJson: String,
+ clientDataHash: ByteArray?,
+ preferImmediatelyAvailableCredentials: Boolean,
+ origin: String?,
+ preferDefaultProvider: String?
+ ) : this(requestJson, clientDataHash, preferImmediatelyAvailableCredentials,
+ getRequestDisplayInfo(requestJson, preferDefaultProvider), origin)
+
init {
require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
}
/** @hide */
companion object {
- internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
internal const val BUNDLE_KEY_CLIENT_DATA_HASH =
"androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
@@ -92,14 +125,22 @@
"androidx.credentials.BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST"
@JvmStatic
- internal fun getRequestDisplayInfo(requestJson: String): DisplayInfo {
+ internal fun getRequestDisplayInfo(
+ requestJson: String,
+ defaultProvider: String? = null,
+ ): DisplayInfo {
return try {
val json = JSONObject(requestJson)
val user = json.getJSONObject("user")
val userName = user.getString("name")
val displayName: String? =
if (user.isNull("displayName")) null else user.getString("displayName")
- DisplayInfo(userName, displayName)
+ DisplayInfo(
+ userId = userName,
+ userDisplayName = displayName,
+ credentialTypeIcon = null,
+ preferDefaultProvider = defaultProvider,
+ )
} catch (e: Exception) {
throw IllegalArgumentException("user.name must be defined in requestJson")
}
@@ -108,8 +149,7 @@
@JvmStatic
internal fun toCredentialDataBundle(
requestJson: String,
- clientDataHash: String? = null,
- preferImmediatelyAvailableCredentials: Boolean
+ clientDataHash: ByteArray? = null,
): Bundle {
val bundle = Bundle()
bundle.putString(
@@ -117,19 +157,14 @@
BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST
)
bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
- bundle.putBoolean(
- BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials
- )
+ bundle.putByteArray(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
return bundle
}
@JvmStatic
internal fun toCandidateDataBundle(
requestJson: String,
- clientDataHash: String?,
- preferImmediatelyAvailableCredentials: Boolean
+ clientDataHash: ByteArray?,
): Bundle {
val bundle = Bundle()
bundle.putString(
@@ -137,35 +172,29 @@
BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST
)
bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
- bundle.putBoolean(
- BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials
- )
+ bundle.putByteArray(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
return bundle
}
- @Suppress("deprecation") // bundle.get() used for boolean value
- // to prevent default boolean value from being returned.
@JvmStatic
@RequiresApi(23)
- internal fun createFrom(data: Bundle, origin: String? = null):
+ internal fun createFrom(data: Bundle, origin: String?):
CreatePublicKeyCredentialRequest {
try {
val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
+ val clientDataHash = data.getByteArray(BUNDLE_KEY_CLIENT_DATA_HASH)
val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
+ data.getBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS, false)
val displayInfo = DisplayInfo.parseFromCredentialDataBundle(data)
return if (displayInfo == null) CreatePublicKeyCredentialRequest(
requestJson!!,
clientDataHash,
- (preferImmediatelyAvailableCredentials!!) as Boolean,
+ preferImmediatelyAvailableCredentials,
origin
) else CreatePublicKeyCredentialRequest(
requestJson!!,
clientDataHash,
- (preferImmediatelyAvailableCredentials!!) as Boolean,
+ preferImmediatelyAvailableCredentials,
displayInfo,
origin
)
diff --git a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
index 404ec2f..36d9558 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
@@ -17,19 +17,19 @@
package androidx.credentials
import android.os.Bundle
-import androidx.annotation.RestrictTo
import androidx.credentials.internal.FrameworkClassParsingException
/**
* Base class for a credential with which the user consented to authenticate to the app.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass (e.g.
+ * [PasswordCredential.TYPE_PASSWORD_CREDENTIAL] for `PasswordCredential` or
+ * [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL] for `PublicKeyCredential`)
+ * @property data the credential data in the [Bundle] format
*/
abstract class Credential internal constructor(
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val type: String,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val data: Bundle,
+ val type: String,
+ val data: Bundle,
) {
/** @hide */
companion object {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
index 53396d9..a73c71c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
@@ -16,15 +16,14 @@
package androidx.credentials
-import android.app.Activity
+import android.annotation.SuppressLint
+import android.app.PendingIntent
import android.content.Context
import android.os.CancellationSignal
+import androidx.annotation.RequiresApi
import androidx.credentials.exceptions.ClearCredentialException
-import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException
import androidx.credentials.exceptions.CreateCredentialException
-import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
import androidx.credentials.exceptions.GetCredentialException
-import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
import java.util.concurrent.Executor
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@@ -85,11 +84,17 @@
*
*
*/
-@Suppress("UNUSED_PARAMETER")
-class CredentialManager private constructor(private val context: Context) {
+@RequiresApi(16)
+@SuppressLint("ObsoleteSdkInt")
+interface CredentialManager {
companion object {
+ /**
+ * Creates a [CredentialManager] based on the given [context].
+ *
+ * @param context the context with which the CredentialManager should be associated
+ */
@JvmStatic
- fun create(context: Context): CredentialManager = CredentialManager(context)
+ fun create(context: Context): CredentialManager = CredentialManagerImpl(context)
}
/**
@@ -98,13 +103,14 @@
* The execution potentially launches framework UI flows for a user to view available
* credentials, consent to using one of them, etc.
*
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
* @param request the request for getting the credential
- * @param activity the activity used to potentially launch any UI needed
* @throws GetCredentialException If the request fails
*/
suspend fun getCredential(
+ context: Context,
request: GetCredentialRequest,
- activity: Activity,
): GetCredentialResponse = suspendCancellableCoroutine { continuation ->
// Any Android API that supports cancellation should be configured to propagate
// coroutine cancellation as follows:
@@ -123,8 +129,97 @@
}
getCredentialAsync(
+ context,
request,
- activity,
+ canceller,
+ // Use a direct executor to avoid extra dispatch. Resuming the continuation will
+ // handle getting to the right thread or pool via the ContinuationInterceptor.
+ Runnable::run,
+ callback)
+ }
+
+ /**
+ * Requests a credential from the user.
+ *
+ * Different from the other `getCredential(GetCredentialRequest, Activity)` API, this API
+ * launches the remaining flows to retrieve an app credential from the user, after the
+ * completed prefetch work corresponding to the given `pendingGetCredentialHandle`. Use this
+ * API to complete the full credential retrieval operation after you initiated a request through
+ * the [prepareGetCredential] API.
+ *
+ * The execution can potentially launch UI flows to collect user consent to using a
+ * credential, display a picker when multiple credentials exist, etc.
+ *
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
+ * @param pendingGetCredentialHandle the handle representing the pending operation to resume
+ * @throws GetCredentialException If the request fails
+ */
+ @RequiresApi(34)
+ suspend fun getCredential(
+ context: Context,
+ pendingGetCredentialHandle: PrepareGetCredentialResponse.PendingGetCredentialHandle,
+ ): GetCredentialResponse = suspendCancellableCoroutine { continuation ->
+ // Any Android API that supports cancellation should be configured to propagate
+ // coroutine cancellation as follows:
+ val canceller = CancellationSignal()
+ continuation.invokeOnCancellation { canceller.cancel() }
+
+ val callback = object : CredentialManagerCallback<GetCredentialResponse,
+ GetCredentialException> {
+ override fun onResult(result: GetCredentialResponse) {
+ continuation.resume(result)
+ }
+
+ override fun onError(e: GetCredentialException) {
+ continuation.resumeWithException(e)
+ }
+ }
+
+ getCredentialAsync(
+ context,
+ pendingGetCredentialHandle,
+ canceller,
+ // Use a direct executor to avoid extra dispatch. Resuming the continuation will
+ // handle getting to the right thread or pool via the ContinuationInterceptor.
+ Runnable::run,
+ callback)
+ }
+
+ /**
+ * Prepares for a get-credential operation. Returns a [PrepareGetCredentialResponse]
+ * that can later be used to launch the credential retrieval UI flow to finalize a user
+ * credential for your app.
+ *
+ * This API doesn't invoke any UI. It only performs the preparation work so that you can
+ * later launch the remaining get-credential operation (involves UIs) through the
+ * [getCredential] API which incurs less latency than executing the whole operation in one call.
+ *
+ * @param request the request for getting the credential
+ * @throws GetCredentialException If the request fails
+ */
+ @RequiresApi(34)
+ suspend fun prepareGetCredential(
+ request: GetCredentialRequest,
+ ): PrepareGetCredentialResponse = suspendCancellableCoroutine { continuation ->
+ // Any Android API that supports cancellation should be configured to propagate
+ // coroutine cancellation as follows:
+ val canceller = CancellationSignal()
+ continuation.invokeOnCancellation { canceller.cancel() }
+
+ val callback = object : CredentialManagerCallback<PrepareGetCredentialResponse,
+ GetCredentialException> {
+ override fun onResult(result: PrepareGetCredentialResponse) {
+ continuation.resume(result)
+ }
+
+ override fun onError(e: GetCredentialException) {
+ continuation.resumeWithException(e)
+ }
+ }
+
+ prepareGetCredentialAsync(
+ request,
canceller,
// Use a direct executor to avoid extra dispatch. Resuming the continuation will
// handle getting to the right thread or pool via the ContinuationInterceptor.
@@ -139,13 +234,14 @@
* The execution potentially launches framework UI flows for a user to view their registration
* options, grant consent, etc.
*
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
* @param request the request for creating the credential
- * @param activity the activity used to potentially launch any UI needed
* @throws CreateCredentialException If the request fails
*/
suspend fun createCredential(
+ context: Context,
request: CreateCredentialRequest,
- activity: Activity,
): CreateCredentialResponse = suspendCancellableCoroutine { continuation ->
// Any Android API that supports cancellation should be configured to propagate
// coroutine cancellation as follows:
@@ -164,8 +260,8 @@
}
createCredentialAsync(
+ context,
request,
- activity,
canceller,
// Use a direct executor to avoid extra dispatch. Resuming the continuation will
// handle getting to the right thread or pool via the ContinuationInterceptor.
@@ -216,73 +312,112 @@
}
/**
- * Java API for requesting a credential from the user.
+ * Requests a credential from the user.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
*
* The execution potentially launches framework UI flows for a user to view available
* credentials, consent to using one of them, etc.
*
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
* @param request the request for getting the credential
- * @param activity an optional activity used to potentially launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this executor
* @param callback the callback invoked when the request succeeds or fails
*/
fun getCredentialAsync(
+ context: Context,
request: GetCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
- ) {
- val provider: CredentialProvider? = CredentialProviderFactory
- .getBestAvailableProvider(context)
- if (provider == null) {
- // TODO (Update with the right error code when ready)
- callback.onError(
- GetCredentialProviderConfigurationException(
- "getCredentialAsync no provider dependencies found - please ensure " +
- "the desired provider dependencies are added")
- )
- return
- }
- provider.onGetCredential(request, activity, cancellationSignal, executor, callback)
- }
+ )
/**
- * Java API for registering a user credential that can be used to authenticate the user to
+ * Requests a credential from the user.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * Different from the other `getCredentialAsync(GetCredentialRequest, Activity)` API, this API
+ * launches the remaining flows to retrieve an app credential from the user, after the
+ * completed prefetch work corresponding to the given `pendingGetCredentialHandle`. Use this
+ * API to complete the full credential retrieval operation after you initiated a request through
+ * the [prepareGetCredentialAsync] API.
+ *
+ * The execution can potentially launch UI flows to collect user consent to using a
+ * credential, display a picker when multiple credentials exist, etc.
+ *
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
+ * @param pendingGetCredentialHandle the handle representing the pending operation to resume
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ fun getCredentialAsync(
+ context: Context,
+ pendingGetCredentialHandle: PrepareGetCredentialResponse.PendingGetCredentialHandle,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
+ )
+
+ /**
+ * Prepares for a get-credential operation. Returns a [PrepareGetCredentialResponse]
+ * that can later be used to launch the credential retrieval UI flow to finalize a user
+ * credential for your app.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * This API doesn't invoke any UI. It only performs the preparation work so that you can
+ * later launch the remaining get-credential operation (involves UIs) through the
+ * [getCredentialAsync] API which incurs less latency than executing the whole operation in one
+ * call.
+ *
+ * @param request the request for getting the credential
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ fun prepareGetCredentialAsync(
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<PrepareGetCredentialResponse, GetCredentialException>,
+ )
+
+ /**
+ * Registers a user credential that can be used to authenticate the user to
* the app in the future.
*
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
* The execution potentially launches framework UI flows for a user to view their registration
* options, grant consent, etc.
*
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
* @param request the request for creating the credential
- * @param activity an optional activity used to potentially launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this executor
* @param callback the callback invoked when the request succeeds or fails
*/
fun createCredentialAsync(
+ context: Context,
request: CreateCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>,
- ) {
- val provider: CredentialProvider? = CredentialProviderFactory
- .getBestAvailableProvider(context)
- if (provider == null) {
- // TODO (Update with the right error code when ready)
- callback.onError(CreateCredentialProviderConfigurationException(
- "createCredentialAsync no provider dependencies found - please ensure the " +
- "desired provider dependencies are added"))
- return
- }
- provider.onCreateCredential(request, activity, cancellationSignal, executor, callback)
- }
+ )
/**
* Clears the current user credential state from all credential providers.
*
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
* You should invoked this api after your user signs out of your app to notify all credential
* providers that any stored credential session for the given app should be cleared.
*
@@ -302,16 +437,12 @@
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<Void?, ClearCredentialException>,
- ) {
- val provider: CredentialProvider? = CredentialProviderFactory
- .getBestAvailableProvider(context)
- if (provider == null) {
- // TODO (Update with the right error code when ready)
- callback.onError(ClearCredentialProviderConfigurationException(
- "clearCredentialStateAsync no provider dependencies found - please ensure the " +
- "desired provider dependencies are added"))
- return
- }
- provider.onClearCredential(request, cancellationSignal, executor, callback)
- }
+ )
+
+ /**
+ * Returns a pending intent that shows a screen that lets a user enable a Credential Manager provider.
+ * @return the pending intent that can be launched
+ */
+ @RequiresApi(34)
+ fun createSettingsPendingIntent(): PendingIntent
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt
new file mode 100644
index 0000000..1b0b198
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.CancellationSignal
+import androidx.annotation.RequiresApi
+import androidx.credentials.exceptions.ClearCredentialException
+import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
+import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
+import java.util.concurrent.Executor
+
+/**
+ * Manages user authentication flows.
+ *
+ * An application can call the CredentialManager apis to launch framework UI flows for a user to
+ * register a new credential or to consent to a saved credential from supported credential
+ * providers, which can then be used to authenticate to the app.
+ *
+ * This class contains its own exception types.
+ * They represent unique failures during the Credential Manager flow. As required, they
+ * can be extended for unique types containing new and unique versions of the exception - either
+ * with new 'exception types' (same credential class, different exceptions), or inner subclasses
+ * and their exception types (a subclass credential class and all their exception types).
+ *
+ * For example, if there is an UNKNOWN exception type, assuming the base Exception is
+ * [ClearCredentialException], we can add an 'exception type' class for it as follows:
+ * TODO("Add in new flow with extensive 'getType' function")
+ * ```
+ * class ClearCredentialUnknownException(
+ * errorMessage: CharSequence? = null
+ * ) : ClearCredentialException(TYPE_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION, errorMessage) {
+ * // ...Any required impl here...//
+ * companion object {
+ * private const val TYPE_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION: String =
+ * "androidx.credentials.TYPE_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION"
+ * }
+ * }
+ * ```
+ *
+ * Furthermore, the base class can be subclassed to a new more specific credential type, which
+ * then can further be subclassed into individual exception types. The first is an example of a
+ * 'inner credential type exception', and the next is a 'exception type' of this subclass exception.
+ *
+ * ```
+ * class UniqueCredentialBasedOnClearCredentialException(
+ * type: String,
+ * errorMessage: CharSequence? = null
+ * ) : ClearCredentialException(type, errorMessage) {
+ * // ... Any required impl here...//
+ * }
+ * // .... code and logic .... //
+ * class UniqueCredentialBasedOnClearCredentialUnknownException(
+ * errorMessage: CharSequence? = null
+ * ) : ClearCredentialException(TYPE_UNIQUE_CREDENTIAL_BASED_ON_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION,
+ * errorMessage) {
+ * // ... Any required impl here ... //
+ * companion object {
+ * private const val
+ * TYPE_UNIQUE_CREDENTIAL_BASED_ON_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION: String =
+ * "androidx.credentials.TYPE_CLEAR_CREDENTIAL_UNKNOWN_EXCEPTION"
+ * }
+ * }
+ * ```
+ *
+ *
+ */
+@RequiresApi(16)
+@SuppressLint("ObsoleteSdkInt")
+internal class CredentialManagerImpl internal constructor(
+ private val context: Context
+) : CredentialManager {
+ companion object {
+ /**
+ * An intent action that shows a screen that let user enable a Credential Manager provider.
+ */
+ private const val
+ INTENT_ACTION_FOR_CREDENTIAL_PROVIDER_SETTINGS: String =
+ "android.settings.CREDENTIAL_PROVIDER"
+ }
+
+ /**
+ * Requests a credential from the user.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * The execution potentially launches framework UI flows for a user to view available
+ * credentials, consent to using one of them, etc.
+ *
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
+ * @param request the request for getting the credential
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ override fun getCredentialAsync(
+ context: Context,
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
+ ) {
+ val provider: CredentialProvider? = CredentialProviderFactory
+ .getBestAvailableProvider(this.context)
+ if (provider == null) {
+ // TODO (Update with the right error code when ready)
+ callback.onError(
+ GetCredentialProviderConfigurationException(
+ "getCredentialAsync no provider dependencies found - please ensure " +
+ "the desired provider dependencies are added")
+ )
+ return
+ }
+ provider.onGetCredential(context, request, cancellationSignal, executor, callback)
+ }
+
+ /**
+ * Requests a credential from the user.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * Different from the other `getCredentialAsync(GetCredentialRequest, Activity)` API, this API
+ * launches the remaining flows to retrieve an app credential from the user, after the
+ * completed prefetch work corresponding to the given `pendingGetCredentialHandle`. Use this
+ * API to complete the full credential retrieval operation after you initiated a request through
+ * the [prepareGetCredentialAsync] API.
+ *
+ * The execution can potentially launch UI flows to collect user consent to using a
+ * credential, display a picker when multiple credentials exist, etc.
+ *
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
+ * @param pendingGetCredentialHandle the handle representing the pending operation to resume
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ override fun getCredentialAsync(
+ context: Context,
+ pendingGetCredentialHandle: PrepareGetCredentialResponse.PendingGetCredentialHandle,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
+ ) {
+ val provider = CredentialProviderFactory.getUAndAboveProvider(context)
+ provider.onGetCredential(
+ context, pendingGetCredentialHandle, cancellationSignal, executor, callback)
+ }
+
+ /**
+ * Prepares for a get-credential operation. Returns a [PrepareGetCredentialResponse]
+ * that can later be used to launch the credential retrieval UI flow to finalize a user
+ * credential for your app.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * This API doesn't invoke any UI. It only performs the preparation work so that you can
+ * later launch the remaining get-credential operation (involves UIs) through the
+ * [getCredentialAsync] API which incurs less latency than executing the whole operation in one
+ * call.
+ *
+ * @param request the request for getting the credential
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ override fun prepareGetCredentialAsync(
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<PrepareGetCredentialResponse, GetCredentialException>,
+ ) {
+ val provider = CredentialProviderFactory.getUAndAboveProvider(context)
+ provider.onPrepareCredential(request, cancellationSignal, executor, callback)
+ }
+
+ /**
+ * Registers a user credential that can be used to authenticate the user to
+ * the app in the future.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * The execution potentially launches framework UI flows for a user to view their registration
+ * options, grant consent, etc.
+ *
+ * @param context the context used to launch any UI needed; use an activity context to make
+ * sure the UI will be launched within the same task stack
+ * @param request the request for creating the credential
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ override fun createCredentialAsync(
+ context: Context,
+ request: CreateCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>,
+ ) {
+ val provider: CredentialProvider? = CredentialProviderFactory
+ .getBestAvailableProvider(this.context)
+ if (provider == null) {
+ // TODO (Update with the right error code when ready)
+ callback.onError(CreateCredentialProviderConfigurationException(
+ "createCredentialAsync no provider dependencies found - please ensure the " +
+ "desired provider dependencies are added"))
+ return
+ }
+ provider.onCreateCredential(context, request, cancellationSignal, executor, callback)
+ }
+
+ /**
+ * Clears the current user credential state from all credential providers.
+ *
+ * This API uses callbacks instead of Kotlin coroutines.
+ *
+ * You should invoked this api after your user signs out of your app to notify all credential
+ * providers that any stored credential session for the given app should be cleared.
+ *
+ * A credential provider may have stored an active credential session and use it to limit
+ * sign-in options for future get-credential calls. For example, it may prioritize the active
+ * credential over any other available credential. When your user explicitly signs out of your
+ * app and in order to get the holistic sign-in options the next time, you should call this API
+ * to let the provider clear any stored credential session.
+ *
+ * @param request the request for clearing the app user's credential state
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ override fun clearCredentialStateAsync(
+ request: ClearCredentialStateRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<Void?, ClearCredentialException>,
+ ) {
+ val provider: CredentialProvider? = CredentialProviderFactory
+ .getBestAvailableProvider(context)
+ if (provider == null) {
+ // TODO (Update with the right error code when ready)
+ callback.onError(ClearCredentialProviderConfigurationException(
+ "clearCredentialStateAsync no provider dependencies found - please ensure the " +
+ "desired provider dependencies are added"))
+ return
+ }
+ provider.onClearCredential(request, cancellationSignal, executor, callback)
+ }
+
+ /**
+ * Returns a pending intent that shows a screen that lets a user enable a Credential Manager provider.
+ * @return the pending intent that can be launched
+ */
+ @RequiresApi(34)
+ override fun createSettingsPendingIntent(): PendingIntent {
+ val intent: Intent = Intent(INTENT_ACTION_FOR_CREDENTIAL_PROVIDER_SETTINGS)
+ intent.setData(Uri.parse("package:" + context.getPackageName()))
+ return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialOption.kt
index 4655ca9..9a1b6ad 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialOption.kt
@@ -16,8 +16,8 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
-import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.credentials.internal.FrameworkClassParsingException
@@ -26,30 +26,36 @@
*
* [GetCredentialRequest] will be composed of a list of [CredentialOption] subclasses to indicate
* the specific credential types and configurations that your app accepts.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass (e.g.
+ * the type for [GetPasswordOption] is [PasswordCredential.TYPE_PASSWORD_CREDENTIAL] and for
+ * [GetPublicKeyCredentialOption] is [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL])
+ * @property requestData the request data in the [Bundle] format
+ * @property candidateQueryData the partial request data in the [Bundle] format that will be sent to
+ * the provider during the initial candidate query stage, which will not contain sensitive user
+ * information
+ * @property isSystemProviderRequired true if must only be fulfilled by a system provider and false
+ * otherwise
+ * @property isAutoSelectAllowed whether a credential entry will be automatically chosen if it is
+ * the only one available option
+ * @property allowedProviders a set of provider service [ComponentName] allowed to receive this
+ * option (Note: a [SecurityException] will be thrown if it is set as non-empty but your app does
+ * not have android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS; for API level < 34,
+ * this property will not take effect and you should control the allowed provider via
+ * [library dependencies](https://developer.android.com/training/sign-in/passkeys#add-dependencies))
*/
abstract class CredentialOption internal constructor(
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val type: String,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val requestData: Bundle,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val candidateQueryData: Bundle,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val isSystemProviderRequired: Boolean,
- /** @hide */
- @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- open val isAutoSelectAllowed: Boolean,
+ val type: String,
+ val requestData: Bundle,
+ val candidateQueryData: Bundle,
+ val isSystemProviderRequired: Boolean,
+ val isAutoSelectAllowed: Boolean,
+ val allowedProviders: Set<ComponentName>,
) {
init {
- @Suppress("UNNECESSARY_SAFE_CALL")
- requestData?.let {
- it.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
- }
+ requestData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
+ candidateQueryData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
}
/** @hide */
@@ -59,23 +65,29 @@
const val BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED =
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
+ internal fun extractAutoSelectValue(data: Bundle): Boolean {
+ return data.getBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)
+ }
+
/** @hide */
@JvmStatic
fun createFrom(
type: String,
requestData: Bundle,
candidateQueryData: Bundle,
- requireSystemProvider: Boolean
+ requireSystemProvider: Boolean,
+ allowedProviders: Set<ComponentName>,
): CredentialOption {
return try {
when (type) {
PasswordCredential.TYPE_PASSWORD_CREDENTIAL ->
- GetPasswordOption.createFrom(requestData)
+ GetPasswordOption.createFrom(requestData, allowedProviders)
PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
when (requestData.getString(PublicKeyCredential.BUNDLE_KEY_SUBTYPE)) {
GetPublicKeyCredentialOption
.BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION ->
- GetPublicKeyCredentialOption.createFrom(requestData)
+ GetPublicKeyCredentialOption.createFrom(
+ requestData, allowedProviders)
else -> throw FrameworkClassParsingException()
}
else -> throw FrameworkClassParsingException()
@@ -84,11 +96,13 @@
// Parsing failed but don't crash the process. Instead just output a request with
// the raw framework values.
GetCustomCredentialOption(
- type,
- requestData,
- candidateQueryData,
- requireSystemProvider,
- requestData.getBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, false)
+ type = type,
+ requestData = requestData,
+ candidateQueryData = candidateQueryData,
+ isSystemProviderRequired = requireSystemProvider,
+ isAutoSelectAllowed = requestData.getBoolean(
+ BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, false),
+ allowedProviders = allowedProviders,
)
}
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
index 49085d8..1dab141 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
@@ -16,8 +16,9 @@
package androidx.credentials
-import android.app.Activity
+import android.content.Context
import android.os.CancellationSignal
+import androidx.annotation.RequiresApi
import androidx.credentials.exceptions.ClearCredentialException
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.GetCredentialException
@@ -50,15 +51,15 @@
/**
* Invoked on a request to get a credential.
*
+ * @param context the client calling context used to potentially launch any UI needed
* @param request the request for getting the credential
- * @param activity the client calling activity used to potentially launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this executor
* @param callback the callback invoked when the request succeeds or fails
*/
fun onGetCredential(
+ context: Context,
request: GetCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
@@ -67,15 +68,15 @@
/**
* Invoked on a request to create a credential.
*
+ * @param context the client calling context used to potentially launch any UI needed
* @param request the request for creating the credential
- * @param activity the client calling activity used to potentially launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this executor
* @param callback the callback invoked when the request succeeds or fails
*/
fun onCreateCredential(
+ context: Context,
request: CreateCredentialRequest,
- activity: Activity,
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>,
@@ -98,4 +99,38 @@
executor: Executor,
callback: CredentialManagerCallback<Void?, ClearCredentialException>,
)
+
+ /**
+ * Invoked on a request to prepare for a get-credential operation
+ *
+ * @param request the request for getting the credential
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ fun onPrepareCredential(
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<PrepareGetCredentialResponse, GetCredentialException>,
+ ) {}
+
+ /**
+ * Complete on a request to get a credential represented by the [pendingGetCredentialHandle].
+ *
+ * @param context the client calling context used to potentially launch any UI needed
+ * @param pendingGetCredentialHandle the handle representing the pending operation to resume
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this executor
+ * @param callback the callback invoked when the request succeeds or fails
+ */
+ @RequiresApi(34)
+ fun onGetCredential(
+ context: Context,
+ pendingGetCredentialHandle: PrepareGetCredentialResponse.PendingGetCredentialHandle,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
+ ) {}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
index cb5b9f9..662acf8 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
@@ -18,7 +18,11 @@
import android.content.Context
import android.content.pm.PackageManager
+import android.os.Build
import android.util.Log
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
/**
* Factory that returns the credential provider to be used by Credential Manager.
@@ -28,6 +32,7 @@
class CredentialProviderFactory {
companion object {
private const val TAG = "CredProviderFactory"
+ private const val MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL = Build.VERSION_CODES.TIRAMISU
/** The metadata key to be used when specifying the provider class name in the
* android manifest file. */
@@ -39,8 +44,20 @@
* the app. Developer must not add more than one provider library.
* Post-U, providers will be registered with the framework, and enabled by the user.
*/
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
fun getBestAvailableProvider(context: Context): CredentialProvider? {
- return tryCreatePreUOemProvider(context)
+ if (BuildCompat.isAtLeastU()) {
+ return CredentialProviderFrameworkImpl(context)
+ } else if (Build.VERSION.SDK_INT <= MAX_CRED_MAN_PRE_FRAMEWORK_API_LEVEL) {
+ return tryCreatePreUOemProvider(context)
+ } else {
+ return null
+ }
+ }
+
+ @RequiresApi(34)
+ fun getUAndAboveProvider(context: Context): CredentialProvider {
+ return CredentialProviderFrameworkImpl(context)
}
private fun tryCreatePreUOemProvider(context: Context): CredentialProvider? {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
new file mode 100644
index 0000000..2b934be
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.credentials.CredentialManager
+import android.os.Bundle
+import android.os.CancellationSignal
+import android.os.OutcomeReceiver
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.credentials.exceptions.ClearCredentialException
+import androidx.credentials.exceptions.ClearCredentialUnknownException
+import androidx.credentials.exceptions.ClearCredentialUnsupportedException
+import androidx.credentials.exceptions.CreateCredentialCancellationException
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.CreateCredentialInterruptedException
+import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
+import androidx.credentials.exceptions.CreateCredentialUnknownException
+import androidx.credentials.exceptions.CreateCredentialUnsupportedException
+import androidx.credentials.exceptions.GetCredentialCancellationException
+import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.exceptions.GetCredentialInterruptedException
+import androidx.credentials.exceptions.GetCredentialUnknownException
+import androidx.credentials.exceptions.GetCredentialUnsupportedException
+import androidx.credentials.exceptions.NoCredentialException
+import androidx.credentials.internal.FrameworkImplHelper
+import java.util.concurrent.Executor
+
+/**
+ * Framework credential provider implementation that allows credential
+ * manager requests to be routed to the framework.
+ *
+ * @hide
+ */
+@RequiresApi(34)
+class CredentialProviderFrameworkImpl(context: Context) : CredentialProvider {
+ private val credentialManager: CredentialManager? =
+ context.getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager?
+
+ override fun onPrepareCredential(
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<PrepareGetCredentialResponse, GetCredentialException>
+ ) {
+ if (isCredmanDisabled {
+ callback.onError(
+ GetCredentialUnsupportedException(
+ "Your device doesn't support credential manager"
+ )
+ )
+ }) return
+ val outcome = object : OutcomeReceiver<
+ android.credentials.PrepareGetCredentialResponse,
+ android.credentials.GetCredentialException> {
+ override fun onResult(response: android.credentials.PrepareGetCredentialResponse) {
+ callback.onResult(convertPrepareGetResponseToJetpackClass(response))
+ }
+
+ override fun onError(error: android.credentials.GetCredentialException) {
+ callback.onError(convertToJetpackGetException(error))
+ }
+ }
+
+ credentialManager!!.prepareGetCredential(
+ convertGetRequestToFrameworkClass(request),
+ cancellationSignal,
+ executor,
+ outcome
+ )
+ }
+
+ override fun onGetCredential(
+ context: Context,
+ pendingGetCredentialHandle: PrepareGetCredentialResponse.PendingGetCredentialHandle,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>
+ ) {
+ if (isCredmanDisabled {
+ callback.onError(
+ GetCredentialUnsupportedException(
+ "Your device doesn't support credential manager"
+ )
+ )
+ }) return
+ val outcome = object : OutcomeReceiver<
+ android.credentials.GetCredentialResponse, android.credentials.GetCredentialException> {
+ override fun onResult(response: android.credentials.GetCredentialResponse) {
+ callback.onResult(convertGetResponseToJetpackClass(response))
+ }
+
+ override fun onError(error: android.credentials.GetCredentialException) {
+ callback.onError(convertToJetpackGetException(error))
+ }
+ }
+
+ credentialManager!!.getCredential(
+ context,
+ pendingGetCredentialHandle.frameworkHandle!!,
+ cancellationSignal,
+ executor,
+ outcome
+ )
+ }
+
+ override fun onGetCredential(
+ context: Context,
+ request: GetCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>
+ ) {
+ if (isCredmanDisabled {
+ callback.onError(
+ GetCredentialUnsupportedException(
+ "Your device doesn't support credential manager"
+ )
+ )
+ }) return
+
+ val outcome = object : OutcomeReceiver<
+ android.credentials.GetCredentialResponse, android.credentials.GetCredentialException> {
+ override fun onResult(response: android.credentials.GetCredentialResponse) {
+ Log.i(TAG, "GetCredentialResponse returned from framework")
+ callback.onResult(convertGetResponseToJetpackClass(response))
+ }
+
+ override fun onError(error: android.credentials.GetCredentialException) {
+ Log.i(TAG, "GetCredentialResponse error returned from framework")
+ callback.onError(convertToJetpackGetException(error))
+ }
+ }
+
+ credentialManager!!.getCredential(
+ context,
+ convertGetRequestToFrameworkClass(request),
+ cancellationSignal,
+ executor,
+ outcome
+ )
+ }
+
+ private fun isCredmanDisabled(handleNullCredMan: () -> Unit): Boolean {
+ if (credentialManager == null) {
+ handleNullCredMan()
+ return true
+ }
+ return false
+ }
+
+ override fun onCreateCredential(
+ context: Context,
+ request: CreateCredentialRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>
+ ) {
+ if (isCredmanDisabled {
+ callback.onError(
+ CreateCredentialUnsupportedException(
+ "Your device doesn't support credential manager"
+ )
+ )
+ }) return
+ val outcome = object : OutcomeReceiver<
+ android.credentials.CreateCredentialResponse,
+ android.credentials.CreateCredentialException> {
+ override fun onResult(response: android.credentials.CreateCredentialResponse) {
+ Log.i(TAG, "Create Result returned from framework: ")
+ callback.onResult(
+ CreateCredentialResponse.createFrom(
+ request.type, response.data
+ )
+ )
+ }
+
+ override fun onError(error: android.credentials.CreateCredentialException) {
+ Log.i(TAG, "CreateCredentialResponse error returned from framework")
+ callback.onError(convertToJetpackCreateException(error))
+ }
+ }
+
+ credentialManager!!.createCredential(
+ context,
+ convertCreateRequestToFrameworkClass(request, context),
+ cancellationSignal,
+ executor,
+ outcome
+ )
+ }
+
+ private fun convertCreateRequestToFrameworkClass(
+ request: CreateCredentialRequest,
+ context: Context
+ ): android.credentials.CreateCredentialRequest {
+ val createCredentialRequestBuilder: android.credentials.CreateCredentialRequest.Builder =
+ android.credentials.CreateCredentialRequest
+ .Builder(request.type,
+ FrameworkImplHelper.getFinalCreateCredentialData(request, context),
+ request.candidateQueryData)
+ .setIsSystemProviderRequired(request.isSystemProviderRequired)
+ // TODO("change to taking value from the request when ready")
+ .setAlwaysSendAppInfoToProvider(true)
+ setOriginForCreateRequest(request, createCredentialRequestBuilder)
+ return createCredentialRequestBuilder.build()
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun setOriginForCreateRequest(
+ request: CreateCredentialRequest,
+ builder: android.credentials.CreateCredentialRequest.Builder
+ ) {
+ if (request.origin != null) {
+ builder.setOrigin(request.origin)
+ }
+ }
+
+ private fun convertGetRequestToFrameworkClass(request: GetCredentialRequest):
+ android.credentials.GetCredentialRequest {
+ val builder = android.credentials.GetCredentialRequest.Builder(
+ GetCredentialRequest.toRequestDataBundle(request))
+ request.credentialOptions.forEach {
+ // TODO(b/278308121): clean up the temporary bundle value injection after the Beta 2
+ // release.
+ if (request.preferImmediatelyAvailableCredentials &&
+ it is GetPublicKeyCredentialOption) {
+ it.requestData.putBoolean(
+ "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS",
+ true,
+ )
+ }
+
+ builder.addCredentialOption(
+ android.credentials.CredentialOption.Builder(
+ it.type, it.requestData, it.candidateQueryData
+ ).setIsSystemProviderRequired(
+ it.isSystemProviderRequired
+ ).setAllowedProviders(it.allowedProviders).build()
+ )
+ }
+ setOriginForGetRequest(request, builder)
+ return builder.build()
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun setOriginForGetRequest(
+ request: GetCredentialRequest,
+ builder: android.credentials.GetCredentialRequest.Builder
+ ) {
+ if (request.origin != null) {
+ builder.setOrigin(request.origin)
+ }
+ }
+
+ private fun createFrameworkClearCredentialRequest():
+ android.credentials.ClearCredentialStateRequest {
+ return android.credentials.ClearCredentialStateRequest(Bundle())
+ }
+
+ internal fun convertToJetpackGetException(error: android.credentials.GetCredentialException):
+ GetCredentialException {
+ return when (error.type) {
+ android.credentials.GetCredentialException.TYPE_NO_CREDENTIAL ->
+ NoCredentialException(error.message)
+
+ android.credentials.GetCredentialException.TYPE_USER_CANCELED ->
+ GetCredentialCancellationException(error.message)
+
+ android.credentials.GetCredentialException.TYPE_INTERRUPTED ->
+ GetCredentialInterruptedException(error.message)
+
+ else -> GetCredentialUnknownException(error.message)
+ }
+ }
+
+ internal fun convertToJetpackCreateException(
+ error: android.credentials.CreateCredentialException
+ ): CreateCredentialException {
+ return when (error.type) {
+ android.credentials.CreateCredentialException.TYPE_NO_CREATE_OPTIONS ->
+ CreateCredentialNoCreateOptionException(error.message)
+
+ android.credentials.CreateCredentialException.TYPE_USER_CANCELED ->
+ CreateCredentialCancellationException(error.message)
+
+ android.credentials.CreateCredentialException.TYPE_INTERRUPTED ->
+ CreateCredentialInterruptedException(error.message)
+
+ else -> CreateCredentialUnknownException(error.message)
+ }
+ }
+
+ internal fun convertGetResponseToJetpackClass(
+ response: android.credentials.GetCredentialResponse
+ ): GetCredentialResponse {
+ val credential = response.credential
+ return GetCredentialResponse(
+ Credential.createFrom(
+ credential.type, credential.data
+ )
+ )
+ }
+
+ internal fun convertPrepareGetResponseToJetpackClass(
+ response: android.credentials.PrepareGetCredentialResponse
+ ): PrepareGetCredentialResponse {
+ return PrepareGetCredentialResponse(
+ response,
+ PrepareGetCredentialResponse.PendingGetCredentialHandle(
+ response.pendingGetCredentialHandle,
+ )
+ )
+ }
+
+ override fun isAvailableOnDevice(): Boolean {
+ // TODO("b/276492529 Base it on API level check")
+ return true
+ }
+
+ override fun onClearCredential(
+ request: ClearCredentialStateRequest,
+ cancellationSignal: CancellationSignal?,
+ executor: Executor,
+ callback: CredentialManagerCallback<Void?, ClearCredentialException>
+ ) {
+ Log.i(TAG, "In CredentialProviderFrameworkImpl onClearCredential")
+
+ if (isCredmanDisabled { ->
+ callback.onError(
+ ClearCredentialUnsupportedException(
+ "Your device doesn't support credential manager"
+ )
+ )
+ }) return
+
+ val outcome = object : OutcomeReceiver<Void,
+ android.credentials.ClearCredentialStateException> {
+ override fun onResult(response: Void) {
+ Log.i(TAG, "Clear result returned from framework: ")
+ callback.onResult(response)
+ }
+
+ override fun onError(error: android.credentials.ClearCredentialStateException) {
+ Log.i(TAG, "ClearCredentialStateException error returned from framework")
+ // TODO("Covert to the appropriate exception")
+ callback.onError(ClearCredentialUnknownException())
+ }
+ }
+
+ credentialManager!!.clearCredentialState(
+ createFrameworkClearCredentialRequest(),
+ cancellationSignal,
+ executor,
+ outcome
+ )
+ }
+
+ /** @hide */
+ companion object {
+ private const val TAG = "CredManProvService"
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CustomCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/CustomCredential.kt
index 7550543..a229a32 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CustomCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CustomCredential.kt
@@ -30,15 +30,17 @@
* Note: The Bundle keys for [data] should not be in the form of `androidx.credentials.*` as they
* are reserved for internal use by this androidx library.
*
- * @property type the credential type determined by the credential-type-specific subclass for custom
+ * @param type the credential type determined by the credential-type-specific subclass for custom
* use cases
- * @property data the credential data in the [Bundle] format for custom use cases
+ * @param data the credential data in the [Bundle] format for custom use cases (note: bundle keys in
+ * the form of `androidx.credentials.*` and `android.credentials.*` are reserved for internal
+ * library usage)
* @throws IllegalArgumentException If [type] is empty
* @throws NullPointerException If [data] or [type] is null
*/
open class CustomCredential(
- final override val type: String,
- final override val data: Bundle
+ type: String,
+ data: Bundle
) : Credential(type, data) {
init {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
index 225880c..f154951 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
@@ -16,6 +16,10 @@
package androidx.credentials
+import android.content.ComponentName
+import android.os.Bundle
+import androidx.credentials.internal.FrameworkClassParsingException
+
/**
* Encapsulates a request to get a user credential.
*
@@ -28,12 +32,41 @@
* @property origin the origin of a different application if the request is being made on behalf of
* that application. For API level >=34, setting a non-null value for this parameter, will throw
* a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present.
+ * @property preferIdentityDocUi the value which signals if the UI should be tailored to display an
+ * identity document like driver license etc.
+ * @property preferUiBrandingComponentName a service [ComponentName] from which the Credential
+ * Selector UI will pull its label and icon to render top level branding. Your app must have the
+ * permission android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or
+ * it would not take effect. Notice that this bit may not take effect for Android API level
+ * 33 and below, depending on the pre-34 provider(s) you have chosen.
+ * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available credentials instead of falling back to discovering remote
+ * options, and false (default) otherwise
+ * @param credentialOptions the list of [CredentialOption] from which the user can choose
+ * one to authenticate to the app
+ * @param origin the origin of a different application if the request is being made on behalf of
+ * that application (Note: for API level >=34, setting a non-null value for this parameter, will
+ * throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not present)
+ * @param preferIdentityDocUi the value which signals if the UI should be tailored to display an
+ * identity document like driver license etc
+ * @param preferUiBrandingComponentName a service [ComponentName] from which the Credential
+ * Selector UI will pull its label and icon to render top level branding (Note: your app must have
+ * the permission android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this, or
+ * it would not take effect; also this bit may not take effect for Android API level 33 and below,
+ * depending on the pre-34 provider(s) you have chosen
+ * @param preferImmediatelyAvailableCredentials true if you prefer the operation to return
+ * immediately when there is no available credentials instead of falling back to discovering remote
+ * options, and false (default) otherwise
* @throws IllegalArgumentException If [credentialOptions] is empty
*/
class GetCredentialRequest
@JvmOverloads constructor(
val credentialOptions: List<CredentialOption>,
val origin: String? = null,
+ val preferIdentityDocUi: Boolean = false,
+ val preferUiBrandingComponentName: ComponentName? = null,
+ @get:JvmName("preferImmediatelyAvailableCredentials")
+ val preferImmediatelyAvailableCredentials: Boolean = false,
) {
init {
@@ -44,6 +77,9 @@
class Builder {
private var credentialOptions: MutableList<CredentialOption> = mutableListOf()
private var origin: String? = null
+ private var preferIdentityDocUi: Boolean = false
+ private var preferImmediatelyAvailableCredentials: Boolean = false
+ private var preferUiBrandingComponentName: ComponentName? = null
/** Adds a specific type of [CredentialOption]. */
fun addCredentialOption(credentialOption: CredentialOption): Builder {
@@ -57,22 +93,116 @@
return this
}
- /** Sets the [origin] of a different application if the request is being made on behalf of
+ /**
+ * Sets the [origin] of a different application if the request is being made on behalf of
* that application. For API level >=34, setting a non-null value for this parameter, will
* throw a SecurityException if android.permission.CREDENTIAL_MANAGER_SET_ORIGIN is not
- * present. */
+ * present.
+ */
fun setOrigin(origin: String): Builder {
this.origin = origin
return this
}
/**
+ * Sets whether you prefer the operation to return immediately when there is no available
+ * credentials instead of falling back to discovering remote options. The default value
+ * is false.
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setPreferImmediatelyAvailableCredentials(
+ preferImmediatelyAvailableCredentials: Boolean
+ ): Builder {
+ this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
+ return this
+ }
+
+ /**
+ * Sets service [ComponentName] from which the Credential Selector UI will pull its label
+ * and icon to render top level branding. Your app must have the
+ * permission android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS to specify this,
+ * or it would not take effect. Notice that this bit may not take effect for Android API
+ * level 33 and below, depending on the pre-34 provider(s) you have chosen.
+ */
+ fun setPreferUiBrandingComponentName(component: ComponentName?): Builder {
+ this.preferUiBrandingComponentName = component
+ return this
+ }
+
+ /**
+ * Sets the [Boolean] preferIdentityDocUi to true if the requester wants to prefer using a
+ * UI suited for Identity Documents like mDocs, Driving License etc.
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setPreferIdentityDocUi(preferIdentityDocUi: Boolean): Builder {
+ this.preferIdentityDocUi = preferIdentityDocUi
+ return this
+ }
+
+ /**
* Builds a [GetCredentialRequest].
*
* @throws IllegalArgumentException If [credentialOptions] is empty
*/
fun build(): GetCredentialRequest {
- return GetCredentialRequest(credentialOptions.toList(), origin)
+ return GetCredentialRequest(
+ credentialOptions.toList(),
+ origin,
+ preferIdentityDocUi,
+ preferUiBrandingComponentName,
+ preferImmediatelyAvailableCredentials
+ )
+ }
+ }
+
+ /** @hide */
+ companion object {
+ internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
+ "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
+ internal const val BUNDLE_KEY_PREFER_IDENTITY_DOC_UI =
+ "androidx.credentials.BUNDLE_KEY_PREFER_IDENTITY_DOC_UI"
+ internal const val BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME =
+ "androidx.credentials.BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME"
+
+ /** @hide */
+ @JvmStatic
+ fun toRequestDataBundle(
+ request: GetCredentialRequest
+ ): Bundle {
+ val bundle = Bundle()
+ bundle.putBoolean(BUNDLE_KEY_PREFER_IDENTITY_DOC_UI, request.preferIdentityDocUi)
+ bundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
+ request.preferImmediatelyAvailableCredentials)
+ bundle.putParcelable(
+ BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME, request.preferUiBrandingComponentName)
+ return bundle
+ }
+
+ /** @hide */
+ @JvmStatic
+ fun createFrom(
+ credentialOptions: List<CredentialOption>,
+ origin: String?,
+ data: Bundle
+ ): GetCredentialRequest {
+ try {
+ val preferIdentityDocUi = data.getBoolean(BUNDLE_KEY_PREFER_IDENTITY_DOC_UI)
+ val preferImmediatelyAvailableCredentials = data.getBoolean(
+ BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
+ @Suppress("DEPRECATION")
+ val preferUiBrandingComponentName = data.getParcelable<ComponentName>(
+ BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME)
+ var getCredentialBuilder = Builder().setCredentialOptions(credentialOptions)
+ .setPreferIdentityDocUi(preferIdentityDocUi)
+ .setPreferUiBrandingComponentName(preferUiBrandingComponentName)
+ .setPreferImmediatelyAvailableCredentials(preferImmediatelyAvailableCredentials)
+ if (origin != null) {
+ getCredentialBuilder.setOrigin(origin)
+ }
+ return getCredentialBuilder.build()
+ } catch (e: Exception) {
+ throw FrameworkClassParsingException()
+ }
}
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetCustomCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetCustomCredentialOption.kt
index 775e331..5ced43a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetCustomCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetCustomCredentialOption.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
/**
@@ -29,37 +30,44 @@
* Note: The Bundle keys for [requestData] and [candidateQueryData] should not be in the form of
* `androidx.credentials.*` as they are reserved for internal use by this androidx library.
*
- * @property type the credential type determined by the credential-type-specific subclass
+ * @param type the credential type determined by the credential-type-specific subclass
* generated for custom use cases
- * @property requestData the request data in the [Bundle] format, generated for custom use cases
- * @property candidateQueryData the partial request data in the [Bundle] format that will be sent to
+ * @param requestData the request data in the [Bundle] format, generated for custom use cases
+ * (note: bundle keys in the form of `androidx.credentials.*` and `android.credentials.*` are
+ * reserved for internal library usage)
+ * @param candidateQueryData the partial request data in the [Bundle] format that will be sent to
* the provider during the initial candidate query stage, which should not contain sensitive user
- * information
- * @property isSystemProviderRequired true if must only be fulfilled by a system provider and false
+ * information (note: bundle keys in the form of `androidx.credentials.*` and
+ * `android.credentials.*` are reserved for internal library usage)
+ * @param isSystemProviderRequired true if must only be fulfilled by a system provider and false
* otherwise
- * @property isAutoSelectAllowed defines if a credential entry will be automatically chosen if it is
+ * @param isAutoSelectAllowed defines if a credential entry will be automatically chosen if it is
* the only one available option, false by default
+ * @param allowedProviders a set of provider service [ComponentName] allowed to receive this
+ * option (Note: a [SecurityException] will be thrown if it is set as non-empty but your app does
+ * not have android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS; for API level < 34,
+ * this property will not take effect and you should control the allowed provider via
+ * [library dependencies](https://developer.android.com/training/sign-in/passkeys#add-dependencies))
* @throws IllegalArgumentException If [type] is empty
* @throws NullPointerException If [requestData] or [type] is null
*/
open class GetCustomCredentialOption @JvmOverloads constructor(
- final override val type: String,
- final override val requestData: Bundle,
- final override val candidateQueryData: Bundle,
- final override val isSystemProviderRequired: Boolean,
- final override val isAutoSelectAllowed: Boolean = false,
+ type: String,
+ requestData: Bundle,
+ candidateQueryData: Bundle,
+ isSystemProviderRequired: Boolean,
+ isAutoSelectAllowed: Boolean = false,
+ allowedProviders: Set<ComponentName> = emptySet(),
) : CredentialOption(
type = type,
requestData = requestData,
candidateQueryData = candidateQueryData,
isSystemProviderRequired = isSystemProviderRequired,
- isAutoSelectAllowed = isAutoSelectAllowed
+ isAutoSelectAllowed = isAutoSelectAllowed,
+ allowedProviders = allowedProviders,
) {
init {
- if (!requestData.containsKey(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED)) {
- requestData.putBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, isAutoSelectAllowed)
- }
require(type.isNotEmpty()) { "type should not be empty" }
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
index a65188e..0996838 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
@@ -16,29 +16,60 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
-/** A request to retrieve the user's saved application password from their password provider.
+/**
+ * A request to retrieve the user's saved application password from their password provider.
*
- * @property isAutoSelectAllowed false by default, allows auto selecting a password if there is
+ * @property allowedUserIds a optional set of user ids with which the credentials associated are
+ * requested; leave as empty if you want to request all the available user credentials
+ * @param allowedUserIds a optional set of user ids with which the credentials associated are
+ * requested; leave as empty if you want to request all the available user credentials
+ * @param isAutoSelectAllowed false by default, allows auto selecting a password if there is
* only one available
+ * @param allowedProviders a set of provider service [ComponentName] allowed to receive this
+ * option (Note: a [SecurityException] will be thrown if it is set as non-empty but your app does
+ * not have android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS; for API level < 34,
+ * this property will not take effect and you should control the allowed provider via
+ * [library dependencies](https://developer.android.com/training/sign-in/passkeys#add-dependencies))
*/
class GetPasswordOption @JvmOverloads constructor(
- override val isAutoSelectAllowed: Boolean = false
+ val allowedUserIds: Set<String> = emptySet(),
+ isAutoSelectAllowed: Boolean = false,
+ allowedProviders: Set<ComponentName> = emptySet(),
) : CredentialOption(
type = PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
- requestData = Bundle(),
- candidateQueryData = Bundle(),
+ requestData = toBundle(allowedUserIds),
+ candidateQueryData = toBundle(allowedUserIds),
isSystemProviderRequired = false,
isAutoSelectAllowed = isAutoSelectAllowed,
+ allowedProviders,
) {
/** @hide */
companion object {
- @Suppress("UNUSED_PARAMETER")
+ internal const val BUNDLE_KEY_ALLOWED_USER_IDS =
+ "androidx.credentials.BUNDLE_KEY_ALLOWED_USER_IDS"
+
@JvmStatic
- internal fun createFrom(data: Bundle): GetPasswordOption {
- return GetPasswordOption()
+ internal fun createFrom(
+ data: Bundle,
+ allowedProviders: Set<ComponentName>,
+ ): GetPasswordOption {
+ val allowUserIdList = data.getStringArrayList(BUNDLE_KEY_ALLOWED_USER_IDS)
+ return GetPasswordOption(
+ allowUserIdList?.toSet() ?: emptySet(),
+ data.getBoolean(BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED, false),
+ allowedProviders
+ )
+ }
+
+ @JvmStatic
+ internal fun toBundle(allowUserIds: Set<String>): Bundle {
+ val bundle = Bundle()
+ bundle.putStringArrayList(BUNDLE_KEY_ALLOWED_USER_IDS, ArrayList(allowUserIds))
+ return bundle
}
}
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
index cdd6e76..1ab47ac 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.content.ComponentName
import android.os.Bundle
import androidx.credentials.internal.FrameworkClassParsingException
@@ -24,27 +25,33 @@
*
* @property requestJson the request in JSON format in the standard webauthn web json
* shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
- * @property clientDataHash a hash that is used to verify the relying party identity, set only if
- * you have set the [GetCredentialRequest.origin]
- * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
- * immediately when there is no available credential instead of falling back to discovering remote
- * credentials, and false (default) otherwise
+ * @property clientDataHash a clientDataHash value to sign over in place of assembling and hashing
+ * clientDataJSON during the signature request; meaningful only if you have set the
+ * [GetCredentialRequest.origin]
+ * @param requestJson the request in JSON format in the standard webauthn web json
+ * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
+ * @param clientDataHash a clientDataHash value to sign over in place of assembling and hashing
+ * clientDataJSON during the signature request; set only if you have set the
+ * [GetCredentialRequest.origin]
+ * @param allowedProviders a set of provider service [ComponentName] allowed to receive this
+ * option (Note: a [SecurityException] will be thrown if it is set as non-empty but your app does
+ * not have android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS; for API level < 34,
+ * this property will not take effect and you should control the allowed provider via
+ * [library dependencies](https://developer.android.com/training/sign-in/passkeys#add-dependencies))
* @throws NullPointerException If [requestJson] is null
* @throws IllegalArgumentException If [requestJson] is empty
*/
class GetPublicKeyCredentialOption @JvmOverloads constructor(
val requestJson: String,
- val clientDataHash: String? = null,
- @get:JvmName("preferImmediatelyAvailableCredentials")
- val preferImmediatelyAvailableCredentials: Boolean = false,
+ val clientDataHash: ByteArray? = null,
+ allowedProviders: Set<ComponentName> = emptySet(),
) : CredentialOption(
type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
- requestData = toRequestDataBundle(requestJson, clientDataHash,
- preferImmediatelyAvailableCredentials),
- candidateQueryData = toRequestDataBundle(requestJson, clientDataHash,
- preferImmediatelyAvailableCredentials),
+ requestData = toRequestDataBundle(requestJson, clientDataHash),
+ candidateQueryData = toRequestDataBundle(requestJson, clientDataHash),
isSystemProviderRequired = false,
isAutoSelectAllowed = true,
+ allowedProviders,
) {
init {
require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
@@ -52,8 +59,6 @@
/** @hide */
companion object {
- internal const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
- "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS"
internal const val BUNDLE_KEY_CLIENT_DATA_HASH =
"androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
internal const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
@@ -63,8 +68,7 @@
@JvmStatic
internal fun toRequestDataBundle(
requestJson: String,
- clientDataHash: String?,
- preferImmediatelyAvailableCredentials: Boolean
+ clientDataHash: ByteArray?,
): Bundle {
val bundle = Bundle()
bundle.putString(
@@ -72,24 +76,25 @@
BUNDLE_VALUE_SUBTYPE_GET_PUBLIC_KEY_CREDENTIAL_OPTION
)
bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
- bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
- bundle.putBoolean(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS,
- preferImmediatelyAvailableCredentials)
+ bundle.putByteArray(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
return bundle
}
@Suppress("deprecation") // bundle.get() used for boolean value to prevent default
// boolean value from being returned.
@JvmStatic
- internal fun createFrom(data: Bundle): GetPublicKeyCredentialOption {
+ internal fun createFrom(
+ data: Bundle,
+ allowedProviders: Set<ComponentName>,
+ ): GetPublicKeyCredentialOption {
try {
val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
- val clientDataHash = data.getString(BUNDLE_KEY_CLIENT_DATA_HASH)
- val preferImmediatelyAvailableCredentials =
- data.get(BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS)
- return GetPublicKeyCredentialOption(requestJson!!,
+ val clientDataHash = data.getByteArray(BUNDLE_KEY_CLIENT_DATA_HASH)
+ return GetPublicKeyCredentialOption(
+ requestJson!!,
clientDataHash,
- (preferImmediatelyAvailableCredentials!!) as Boolean)
+ allowedProviders
+ )
} catch (e: Exception) {
throw FrameworkClassParsingException()
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
index 6df28d8..97767e0 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
@@ -17,7 +17,6 @@
package androidx.credentials
import android.os.Bundle
-import androidx.annotation.VisibleForTesting
import androidx.credentials.internal.FrameworkClassParsingException
/**
@@ -38,17 +37,13 @@
require(password.isNotEmpty()) { "password should not be empty" }
}
- /** @hide */
+ /** Companion constants / helpers for [PasswordCredential]. */
companion object {
- // TODO: this type is officially defined in the framework. This definition should be
- // removed when the framework type is available in jetpack.
- /** @hide */
+ /** The type value for password related operations. */
const val TYPE_PASSWORD_CREDENTIAL: String = "android.credentials.TYPE_PASSWORD_CREDENTIAL"
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
- const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
- const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+ internal const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+ internal const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
@JvmStatic
internal fun toBundle(id: String, password: String): Bundle {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PrepareGetCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/PrepareGetCredentialResponse.kt
new file mode 100644
index 0000000..d19cbb9
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/PrepareGetCredentialResponse.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS
+import android.credentials.PrepareGetCredentialResponse
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.core.os.BuildCompat
+
+/**
+ * A response object that indicates the get-credential prefetch work is complete and provides
+ * metadata about it. It can then be used to issue the full credential retrieval flow via the
+ * [CredentialManager.getCredential] (Kotlin) / [CredentialManager.getCredentialAsync] (Java)
+ * method to perform the remaining flows such as consent
+ * collection and credential selection, to officially retrieve a credential.
+ *
+ * For now this API requires Android U (level 34). However, it is designed with backward
+ * compatibility in mind and can potentially be made accessible <34 if any provider decides to
+ * support that.
+ *
+ * @property frameworkResponse the corresponding framework response, guaranteed to be nonnull
+ * at API level >= 34
+ * @property pendingGetCredentialHandle a handle that represents this pending get-credential
+ * operation; pass this handle to [CredentialManager.getCredential] (Kotlin) /
+ * [CredentialManager.getCredentialAsync] (Java) to perform the remaining flows to officially
+ * retrieve a credential.
+ * @throws NullPointerException If [frameworkResponse] is null at API level >= 34.
+ */
+@RequiresApi(34)
+@OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+class PrepareGetCredentialResponse internal constructor(
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ val frameworkResponse: PrepareGetCredentialResponse?,
+ val pendingGetCredentialHandle: PendingGetCredentialHandle,
+) {
+ init {
+ if (BuildCompat.isAtLeastU()) {
+ frameworkResponse!!
+ }
+ }
+
+ /**
+ * Returns true if the user has any candidate credentials for the given {@code credentialType},
+ * and false otherwise.
+ *
+ * Note: this API will always return false at API level < 34.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
+ fun hasCredentialResults(credentialType: String): Boolean {
+ return frameworkResponse?.hasCredentialResults(credentialType) ?: false
+ }
+
+ /**
+ * Returns true if the user has any candidate authentication actions (locked credential
+ * supplier), and false otherwise.
+ *
+ * Note: this API will always return false at API level < 34.
+ */
+ @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
+ fun hasAuthenticationResults(): Boolean {
+ return frameworkResponse?.hasAuthenticationResults() ?: false
+ }
+
+ /**
+ * Returns true if the user has any candidate remote credential results, and false otherwise.
+ *
+ * Note: this API will always return false at API level < 34.
+ */
+ @RequiresPermission(CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS)
+ fun hasRemoteResults(): Boolean {
+ return frameworkResponse?.hasRemoteResults() ?: false
+ }
+
+ /**
+ * A handle that represents a pending get-credential operation. Pass this handle to
+ * [CredentialManager.getCredential] or [CredentialManager.getCredentialAsync] to perform the
+ * remaining flows to officially retrieve a credential.
+ *
+ * @property frameworkHandle the framework handle representing this pending operation. Must not
+ * be null at API level >= 34.
+ * @throws NullPointerException If [frameworkHandle] is null at API level >= 34.
+ */
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ @RequiresApi(34)
+ class PendingGetCredentialHandle(
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ val frameworkHandle:
+ PrepareGetCredentialResponse.PendingGetCredentialHandle?
+ ) {
+ init {
+ if (BuildCompat.isAtLeastU()) {
+ frameworkHandle!!
+ }
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
index d0640fe..c2992eb 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
@@ -17,7 +17,6 @@
package androidx.credentials
import android.os.Bundle
-import androidx.annotation.VisibleForTesting
import androidx.credentials.internal.FrameworkClassParsingException
/**
@@ -41,18 +40,14 @@
"authentication response JSON must not be empty" }
}
- /** @hide */
+ /** Companion constants / helpers for [PublicKeyCredential]. */
companion object {
- /**
- * The type value for public key credential related operations.
- *
- * @hide
- */
+ /** The type value for public key credential related operations. */
const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
"androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
+
/** The Bundle key value for the public key credential subtype (privileged or regular). */
internal const val BUNDLE_KEY_SUBTYPE = "androidx.credentials.BUNDLE_KEY_SUBTYPE"
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON =
"androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON"
diff --git a/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt b/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
index ab39cd4..c533279 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
@@ -38,14 +38,14 @@
@RequiresApi(23)
fun getFinalCreateCredentialData(
request: CreateCredentialRequest,
- activity: Context,
+ context: Context,
): Bundle {
val createCredentialData = request.credentialData
val displayInfoBundle = request.displayInfo.toBundle()
displayInfoBundle.putParcelable(
CreateCredentialRequest.DisplayInfo.BUNDLE_KEY_CREDENTIAL_TYPE_ICON,
Icon.createWithResource(
- activity,
+ context,
when (request) {
is CreatePasswordRequest -> R.drawable.ic_password
is CreatePublicKeyCredentialRequest -> R.drawable.ic_passkey
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
new file mode 100644
index 0000000..39106a6
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.net.Uri
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import java.util.Collections
+
+/**
+ * An actionable entry that is returned as part of the
+ * [android.service.credentials.BeginGetCredentialResponse], and then shown on the user selector.
+ * An action entry is expected to navigate the user to the credential provider's activity, and
+ * ultimately result in a [androidx.credentials.GetCredentialResponse] through that activity.
+ *
+ * When selected, the associated [PendingIntent] is invoked to launch a provider controlled
+ * activity. The activity invoked due to this pending intent will contain the
+ * [android.service.credentials.BeginGetCredentialRequest] as part of the intent extras. Providers
+ * must use [PendingIntentHandler.retrieveBeginGetCredentialRequest] to get the request.
+ *
+ * When the user is done interacting with the activity and the provider has a credential to return,
+ * provider must call [android.app.Activity.setResult] with the result code as
+ * [android.app.Activity.RESULT_OK], and the [android.content.Intent] data that has been prepared
+ * by using [PendingIntentHandler.setGetCredentialResponse], before ending the activity.
+ * If the provider does not have a credential to return, provider must call
+ * [android.app.Activity.setResult] with the result code as [android.app.Activity.RESULT_CANCELED].
+ *
+ * Examples of [Action] entries include an entry that is titled 'Add a new Password', and navigates
+ * to the 'add password' page of the credential provider app, or an entry that is titled
+ * 'Manage Credentials' and navigates to a particular page that lists all credentials, where the
+ * user may end up selecting a credential that the provider can then return.
+ *
+ * @property title the title of the entry
+ * @property pendingIntent the [PendingIntent] that will be invoked when the user selects this entry
+ * @property subtitle the optional subtitle that is displayed on the entry
+ *
+ * @see android.service.credentials.BeginGetCredentialResponse for usage.
+ *
+ * @throws IllegalArgumentException If [title] is empty
+ * @throws NullPointerException If [title] or [pendingIntent] is null
+ */
+class Action constructor(
+ val title: CharSequence,
+ val pendingIntent: PendingIntent,
+ val subtitle: CharSequence? = null,
+) {
+
+ init {
+ require(title.isNotEmpty()) { "title must not be empty" }
+ }
+
+ /**
+ * A builder for [Action]
+ *
+ * @param title the title of this action entry
+ * @param pendingIntent the [PendingIntent] that will be fired when the user selects
+ * this action entry
+ */
+ class Builder constructor(
+ private val title: CharSequence,
+ private val pendingIntent: PendingIntent
+ ) {
+ private var subtitle: CharSequence? = null
+
+ /** Sets a sub title to be shown on the UI with this entry */
+ fun setSubtitle(subtitle: CharSequence?): Builder {
+ this.subtitle = subtitle
+ return this
+ }
+
+ /**
+ * Builds an instance of [Action]
+ *
+ * @throws IllegalArgumentException If [title] is empty
+ */
+ fun build(): Action {
+ return Action(title, pendingIntent, subtitle)
+ }
+ }
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "Action"
+ private const val SLICE_SPEC_REVISION = 0
+ private const val SLICE_SPEC_TYPE = "Action"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.action.HINT_ACTION_TITLE"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.action.HINT_ACTION_SUBTEXT"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT"
+
+ /**
+ * Converts to slice
+ * @hide
+ */
+ @JvmStatic
+ @RequiresApi(28)
+ fun toSlice(
+ action: Action
+ ): Slice {
+ val title = action.title
+ val subtitle = action.subtitle
+ val pendingIntent = action.pendingIntent
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ SLICE_SPEC_TYPE, SLICE_SPEC_REVISION
+ )
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(SLICE_HINT_TITLE)
+ )
+ .addText(
+ subtitle, /*subType=*/null,
+ listOf(SLICE_HINT_SUBTITLE)
+ )
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [Action] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @RequiresApi(28)
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): Action? {
+ var title: CharSequence = ""
+ var subtitle: CharSequence? = null
+ var pendingIntent: PendingIntent? = null
+
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_TITLE)) {
+ title = it.text
+ } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
+ subtitle = it.text
+ } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ }
+ }
+
+ return try {
+ Action(title, pendingIntent!!, subtitle)
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
new file mode 100644
index 0000000..e468930
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.net.Uri
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import java.util.Collections
+
+/**
+ * An entry on the selector, denoting that the provider service is locked and authentication
+ * is needed to proceed.
+ *
+ * Providers should set this entry when the provider app is locked, and no credentials can
+ * be returned.
+ * Providers must set the [PendingIntent] that leads to their unlock activity. When the user
+ * selects this entry, the corresponding [PendingIntent] is fired and the unlock activity is
+ * invoked. Once the provider authentication flow is complete, providers must set
+ * the [android.service.credentials.BeginGetCredentialResponse] containing the unlocked credential
+ * entries, through the [PendingIntentHandler.setBeginGetCredentialResponse] method, before
+ * finishing the activity.
+ * If providers fail to set the [android.service.credentials.BeginGetCredentialResponse], the
+ * system will assume that there are no credentials available and the this entry will be removed
+ * from the selector.
+ *
+ * @property pendingIntent the [PendingIntent] to be invoked if the user selects
+ * this authentication entry on the UI
+ * @property title the title to be shown with this entry on the account selector UI
+ *
+ * @see android.service.credentials.BeginGetCredentialResponse
+ * for usage details.
+ *
+ * @throws NullPointerException If the [pendingIntent] is null
+ */
+class AuthenticationAction constructor(
+ val title: CharSequence,
+ val pendingIntent: PendingIntent,
+) {
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "AuthenticationAction"
+ private const val SLICE_SPEC_REVISION = 0
+ private const val SLICE_SPEC_TYPE = "AuthenticationAction"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.authenticationAction.SLICE_HINT_TITLE"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.authenticationAction.SLICE_HINT_PENDING_INTENT"
+
+ /** @hide **/
+ @RequiresApi(28)
+ @JvmStatic
+ fun toSlice(authenticationAction: AuthenticationAction): Slice {
+ val title = authenticationAction.title
+ val pendingIntent = authenticationAction.pendingIntent
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ SLICE_SPEC_TYPE,
+ SLICE_SPEC_REVISION
+ )
+ )
+ sliceBuilder
+ .addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ .addText(title, /*subType=*/null, listOf(SLICE_HINT_TITLE))
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [AuthenticationAction] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object that contains the information required for
+ * constructing an instance of this class.
+ *
+ * @hide
+ */
+ @RequiresApi(28)
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): AuthenticationAction? {
+ var title: CharSequence? = null
+ var pendingIntent: PendingIntent? = null
+
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ } else if (it.hasHint(SLICE_HINT_TITLE)) {
+ title = it.text
+ }
+ }
+ return try {
+ AuthenticationAction(title!!, pendingIntent!!)
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt
new file mode 100644
index 0000000..f77e1b5
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.utils.BeginCreateCredentialUtil
+
+/**
+ * Abstract request class for beginning a create credential request.
+ *
+ * This class is to be extended by structured create credential requests
+ * such as [BeginCreatePasswordCredentialRequest].
+ */
+abstract class BeginCreateCredentialRequest constructor(
+ val type: String,
+ val candidateQueryData: Bundle,
+ val callingAppInfo: CallingAppInfo?
+) {
+ @RequiresApi(34)
+ private object Api34Impl {
+ private const val REQUEST_KEY = "androidx.credentials.provider.BeginCreateCredentialRequest"
+
+ @JvmStatic
+ @DoNotInline
+ fun writeToBundle(bundle: Bundle, request: BeginCreateCredentialRequest) {
+ bundle.putParcelable(
+ REQUEST_KEY,
+ BeginCreateCredentialUtil.convertToFrameworkRequest(request)
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun readFromBundle(bundle: Bundle): BeginCreateCredentialRequest? {
+ val frameworkRequest = bundle.getParcelable(
+ REQUEST_KEY,
+ android.service.credentials.BeginCreateCredentialRequest::class.java
+ )
+ if (frameworkRequest != null) {
+ return BeginCreateCredentialUtil.convertToJetpackRequest(frameworkRequest)
+ }
+ return null
+ }
+ }
+
+ companion object {
+ /**
+ * Helper method to convert the class to a parcelable [Bundle], in case the class
+ * instance needs to be sent across a process. Consumers of this method should use
+ * [readFromBundle] to reconstruct the class instance back from the bundle returned here.
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun writeToBundle(request: BeginCreateCredentialRequest): Bundle {
+ val bundle = Bundle()
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.writeToBundle(bundle, request)
+ }
+ return bundle
+ }
+
+ /**
+ * Helper method to convert a [Bundle] retrieved through [writeToBundle], back
+ * to an instance of [BeginCreateCredentialRequest].
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun readFromBundle(bundle: Bundle): BeginCreateCredentialRequest? {
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.readFromBundle(bundle)
+ }
+ return null
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt
new file mode 100644
index 0000000..1a586cf
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.utils.BeginCreateCredentialUtil
+
+/**
+ * Response to [BeginCreateCredentialRequest].
+ *
+ * Credential providers must add a list of [CreateEntry], and an
+ * optional [RemoteEntry] to this response.
+ *
+ * Each [CreateEntry] is displayed to the user on the account selector,
+ * as an account option where the given credential can be stored.
+ * A [RemoteEntry] is an entry on the selector, through which user can choose
+ * to create the credential on a remote device.
+ *
+ * @throws IllegalArgumentException If [createEntries] is empty
+ */
+class BeginCreateCredentialResponse constructor(
+ val createEntries: List<CreateEntry> = listOf(),
+ val remoteEntry: RemoteEntry? = null
+) {
+
+ /** Builder for [BeginCreateCredentialResponse]. **/
+ class Builder {
+ private var createEntries: MutableList<CreateEntry> = mutableListOf()
+ private var remoteEntry: RemoteEntry? = null
+
+ /**
+ * Sets the list of create entries to be shown on the UI.
+ *
+ * @throws IllegalArgumentException If [createEntries] is empty.
+ * @throws NullPointerException If [createEntries] is null, or any of its elements
+ * are null.
+ */
+ fun setCreateEntries(createEntries: List<CreateEntry>): Builder {
+ this.createEntries = createEntries.toMutableList()
+ return this
+ }
+
+ /**
+ * Adds an entry to the list of create entries to be shown on the UI.
+ *
+ * @throws NullPointerException If [createEntry] is null.
+ */
+ fun addCreateEntry(createEntry: CreateEntry): Builder {
+ createEntries.add(createEntry)
+ return this
+ }
+
+ /**
+ * Sets a remote create entry to be shown on the UI. Provider must set this entry if they
+ * wish to create the credential on a different device.
+ *
+ * <p> When constructing the {@link CreateEntry] object, the pending intent must be
+ * set such that it leads to an activity that can provide UI to fulfill the request on
+ * a remote device. When user selects this [remoteEntry], the system will
+ * invoke the pending intent set on the [CreateEntry].
+ *
+ * <p> Once the remote credential flow is complete, the [android.app.Activity]
+ * result should be set to [android.app.Activity#RESULT_OK] and an extra with the
+ * [CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESPONSE] key should be populated
+ * with a [android.credentials.CreateCredentialResponse] object.
+ *
+ * <p> Note that as a provider service you will only be able to set a remote entry if :
+ * - Provider service possesses the
+ * [android.Manifest.permission.PROVIDE_REMOTE_CREDENTIALS] permission.
+ * - Provider service is configured as the provider that can provide remote entries.
+ *
+ * If the above conditions are not met, setting back [BeginCreateCredentialResponse]
+ * on the callback from [CredentialProviderService#onBeginCreateCredential]
+ * will throw a [SecurityException].
+ */
+ fun setRemoteEntry(remoteEntry: RemoteEntry?): Builder {
+ this.remoteEntry = remoteEntry
+ return this
+ }
+
+ /**
+ * Builds a new instance of [BeginCreateCredentialResponse].
+ *
+ * @throws IllegalArgumentException If [createEntries] is empty
+ */
+ fun build(): BeginCreateCredentialResponse {
+ return BeginCreateCredentialResponse(createEntries.toList(), remoteEntry)
+ }
+ }
+
+ @RequiresApi(34)
+ private object Api34Impl {
+ private const val REQUEST_KEY =
+ "androidx.credentials.provider.BeginCreateCredentialResponse"
+
+ @JvmStatic
+ @DoNotInline
+ fun writeToBundle(bundle: Bundle, response: BeginCreateCredentialResponse) {
+ bundle.putParcelable(
+ REQUEST_KEY,
+ BeginCreateCredentialUtil.convertToFrameworkResponse(response)
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun readFromBundle(bundle: Bundle): BeginCreateCredentialResponse? {
+ val frameworkResponse = bundle.getParcelable(
+ REQUEST_KEY,
+ android.service.credentials.BeginCreateCredentialResponse::class.java
+ )
+ if (frameworkResponse != null) {
+ return BeginCreateCredentialUtil.convertToJetpackResponse(frameworkResponse)
+ }
+ return null
+ }
+ }
+
+ companion object {
+ /**
+ * Helper method to convert the class to a parcelable [Bundle], in case the class
+ * instance needs to be sent across a process. Consumers of this method should use
+ * [readFromBundle] to reconstruct the class instance back from the bundle returned here.
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun writeToBundle(response: BeginCreateCredentialResponse): Bundle {
+ val bundle = Bundle()
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.writeToBundle(bundle, response)
+ }
+ return bundle
+ }
+
+ /**
+ * Helper method to convert a [Bundle] retrieved through [writeToBundle], back
+ * to an instance of [BeginGetCredentialResponse].
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun readFromBundle(bundle: Bundle): BeginCreateCredentialResponse? {
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.readFromBundle(bundle)
+ }
+ return null
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCustomCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCustomCredentialRequest.kt
new file mode 100644
index 0000000..07edf25
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCustomCredentialRequest.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+
+/**
+ * Base custom begin create request class for registering a credential.
+ *
+ * If you get a [BeginCreateCustomCredentialRequest] instead of a type-safe request class such as
+ * [BeginCreatePasswordCredentialRequest], [BeginCreatePublicKeyCredentialRequest], etc., then
+ * as a credential provider, you should check if you have any other library at interest that
+ * supports this custom [type] of credential request,
+ * and if so use its parsing utilities to resolve to a type-safe class within that library.
+ *
+ * Note: The Bundle keys for [candidateQueryData] should not be in the form
+ * of androidx.credentials.*` as they are reserved for internal use by this androidx library.
+ *
+ * @param type the credential type determined by the credential-type-specific subclass for
+ * custom use cases
+ * @param candidateQueryData the partial request data in the [Bundle] format that will be sent
+ * to the provider during the initial candidate query stage, which should not contain sensitive
+ * user credential information (note: bundle keys in the form of `androidx.credentials.*` are
+ * reserved for internal library use)
+ * @param callingAppInfo info pertaining to the app that is requesting for credentials
+ * retrieval or creation
+ * @throws IllegalArgumentException If [type] is empty
+ * @throws NullPointerException If [type], or [candidateQueryData] is null
+ */
+open class BeginCreateCustomCredentialRequest constructor(
+ type: String,
+ candidateQueryData: Bundle,
+ callingAppInfo: CallingAppInfo?
+) : BeginCreateCredentialRequest(type, candidateQueryData, callingAppInfo)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt
new file mode 100644
index 0000000..41e4a03
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePasswordCredentialRequest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.PasswordCredential
+import androidx.credentials.internal.FrameworkClassParsingException
+
+/**
+ * Request to begin saving a password credential, received by the provider with a
+ * CredentialProviderBaseService.onBeginCreateCredentialRequest call.
+ *
+ * This request will not contain all parameters needed to store the password. Provider must
+ * use the initial parameters to determine if the password can be stored, and return
+ * a list of [CreateEntry], denoting the accounts/groups where the password can be stored.
+ * When user selects one of the returned [CreateEntry], the corresponding [PendingIntent] set on
+ * the [CreateEntry] will be fired. The [Intent] invoked through the [PendingIntent] will contain the
+ * complete [CreatePasswordRequest]. This request will contain all required parameters to
+ * actually store the password.
+ *
+ * @see BeginCreateCredentialRequest
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ */
+class BeginCreatePasswordCredentialRequest constructor(
+ callingAppInfo: CallingAppInfo?,
+ candidateQueryData: Bundle
+) : BeginCreateCredentialRequest(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ candidateQueryData,
+ callingAppInfo,
+) {
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ /** @hide **/
+ @JvmStatic
+ @Suppress("UNUSED_PARAMETER")
+ internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):
+ BeginCreatePasswordCredentialRequest {
+ try {
+ return BeginCreatePasswordCredentialRequest(
+ callingAppInfo, data)
+ } catch (e: Exception) {
+ throw FrameworkClassParsingException()
+ }
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt
new file mode 100644
index 0000000..992c1d0
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreatePublicKeyCredentialRequest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_CLIENT_DATA_HASH
+import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_REQUEST_JSON
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.internal.FrameworkClassParsingException
+
+/**
+ * Request to begin registering a public key credential.
+ *
+ * This request will not contain all parameters needed to create the public key. Provider must
+ * use the initial parameters to determine if the public key can be registered, and return
+ * a list of [CreateEntry], denoting the accounts/groups where the public key can be registered.
+ * When user selects one of the returned [CreateEntry], the corresponding [PendingIntent] set on
+ * the [CreateEntry] will be fired. The [Intent] invoked through the [PendingIntent] will contain
+ * the complete [CreatePublicKeyCredentialRequest]. This request will contain all required
+ * parameters to actually register a public key.
+ *
+ * @property requestJson the request json to be used for registering the public key credential
+ * @property clientDataHash a hash that is used to verify the relying party identity, set only if
+ * [android.service.credentials.CallingAppInfo.getOrigin] is set
+ *
+ * @see BeginCreateCredentialRequest
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ */
+class BeginCreatePublicKeyCredentialRequest @JvmOverloads constructor(
+ val requestJson: String,
+ callingAppInfo: CallingAppInfo?,
+ candidateQueryData: Bundle,
+ val clientDataHash: ByteArray? = null,
+) : BeginCreateCredentialRequest(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ candidateQueryData,
+ callingAppInfo
+) {
+ init {
+ require(requestJson.isNotEmpty()) { "json must not be empty" }
+ initiateBundle(candidateQueryData, requestJson)
+ }
+
+ private fun initiateBundle(candidateQueryData: Bundle, requestJson: String) {
+ candidateQueryData.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+ }
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ /** @hide */
+ @JvmStatic
+ internal fun createFrom(data: Bundle, callingAppInfo: CallingAppInfo?):
+ BeginCreatePublicKeyCredentialRequest {
+ try {
+ val requestJson = data.getString(BUNDLE_KEY_REQUEST_JSON)
+ val clientDataHash = data.getByteArray(BUNDLE_KEY_CLIENT_DATA_HASH)
+ return BeginCreatePublicKeyCredentialRequest(requestJson!!,
+ callingAppInfo, data, clientDataHash)
+ } catch (e: Exception) {
+ throw FrameworkClassParsingException()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt
new file mode 100644
index 0000000..fe7811e
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialOption.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.credentials.PasswordCredential
+import androidx.credentials.PublicKeyCredential
+
+/**
+ * Base class that a credential provider receives during the query phase of a get-credential flow.
+ * Classes derived from this base class contain
+ * parameters required to retrieve a specific type of credential. E.g. [BeginGetPasswordOption]
+ * contains parameters required to retrieve passwords.
+ *
+ * [BeginGetCredentialRequest] will be composed of a list of [BeginGetCredentialOption]
+ * subclasses to indicate the specific credential types and configurations that the credential
+ * provider must include while building the [BeginGetCredentialResponse].
+ *
+ * @property id unique id representing this particular option. Credential providers must
+ * use this Id while constructing the [CredentialEntry] to be set on [BeginGetCredentialResponse]
+ * @property type the type of the credential to be retrieved against this option. E.g. a
+ * [BeginGetPasswordOption] will have type [PasswordCredential.TYPE_PASSWORD_CREDENTIAL]
+ * @property candidateQueryData the parameters needed to retrieve the credentials, in the form of a
+ * [Bundle]. Note that this is a 'Begin' request denoting a query phase. In this phase, only
+ * sensitive information is included in the [candidateQueryData] bundle.
+ */
+abstract class BeginGetCredentialOption internal constructor(
+ val id: String,
+ val type: String,
+ val candidateQueryData: Bundle
+) {
+ /** @hide **/
+ companion object {
+ @JvmStatic
+ internal fun createFrom(id: String, type: String, candidateQueryData: Bundle):
+ BeginGetCredentialOption {
+ return when (type) {
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL -> {
+ BeginGetPasswordOption.createFrom(candidateQueryData, id)
+ }
+
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> {
+ BeginGetPublicKeyCredentialOption.createFrom(candidateQueryData, id)
+ }
+
+ else -> {
+ BeginGetCustomCredentialOption(id, type, candidateQueryData)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt
new file mode 100644
index 0000000..0a04264
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.utils.BeginGetCredentialUtil
+
+/**
+ * Query stage request for getting user's credentials from a given credential provider.
+ *
+ * <p>This request contains a list of [BeginGetCredentialOption] that have parameters
+ * to be used to query credentials, and return a list of [CredentialEntry] to be set
+ * on the [BeginGetCredentialResponse]. This list is then shown to the user on a selector.
+ *
+ * @param beginGetCredentialOptions the list of type specific credential options to to be processed
+ * in order to produce a [BeginGetCredentialResponse]
+ * @param callingAppInfo info pertaining to the app requesting credentials
+ */
+class BeginGetCredentialRequest @JvmOverloads constructor(
+ val beginGetCredentialOptions: List<BeginGetCredentialOption>,
+ val callingAppInfo: CallingAppInfo? = null,
+) {
+ @RequiresApi(34)
+ private object Api34Impl {
+ private const val REQUEST_KEY = "androidx.credentials.provider.BeginGetCredentialRequest"
+
+ @JvmStatic
+ @DoNotInline
+ fun writeToBundle(bundle: Bundle, request: BeginGetCredentialRequest) {
+ bundle.putParcelable(
+ REQUEST_KEY,
+ BeginGetCredentialUtil.convertToFrameworkRequest(request)
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun readFromBundle(bundle: Bundle): BeginGetCredentialRequest? {
+ val frameworkRequest = bundle.getParcelable(
+ REQUEST_KEY,
+ android.service.credentials.BeginGetCredentialRequest::class.java
+ )
+ if (frameworkRequest != null) {
+ return BeginGetCredentialUtil.convertToJetpackRequest(frameworkRequest)
+ }
+ return null
+ }
+ }
+
+ companion object {
+ /**
+ * Helper method to convert the class to a parcelable [Bundle], in case the class
+ * instance needs to be sent across a process. Consumers of this method should use
+ * [readFromBundle] to reconstruct the class instance back from the bundle returned here.
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun writeToBundle(request: BeginGetCredentialRequest): Bundle {
+ val bundle = Bundle()
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.writeToBundle(bundle, request)
+ }
+ return bundle
+ }
+
+ /**
+ * Helper method to convert a [Bundle] retrieved through [writeToBundle], back
+ * to an instance of [BeginGetCredentialRequest].
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun readFromBundle(bundle: Bundle): BeginGetCredentialRequest? {
+ if (BuildCompat.isAtLeastU()) {
+ return Api34Impl.readFromBundle(bundle)
+ }
+ return null
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt
new file mode 100644
index 0000000..7e631f4
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.utils.BeginGetCredentialUtil
+
+/**
+ * Response from a credential provider to [BeginGetCredentialRequest], containing credential
+ * entries and other associated data to be shown on the account selector UI.
+ *
+ * Credential providers can set multiple [CredentialEntry] per [BeginGetCredentialOption]
+ * retrieved from the top level request [BeginGetCredentialRequest]. These entries will appear
+ * to the user on the selector.
+ *
+ * Additionally credential providers can add a list of [AuthenticationAction] if all
+ * credentials for the credential provider are locked. Providers can also set a list of
+ * [Action] that can navigate the user straight to a provider activity where the rest of
+ * the request can be processed.
+ */
+class BeginGetCredentialResponse constructor(
+ val credentialEntries: List<CredentialEntry> = listOf(),
+ val actions: List<Action> = listOf(),
+ val authenticationActions: List<AuthenticationAction> = listOf(),
+ val remoteEntry: RemoteEntry? = null
+) {
+ /** Builder for [BeginGetCredentialResponse]. **/
+ class Builder {
+ private var credentialEntries: MutableList<CredentialEntry> = mutableListOf()
+ private var actions: MutableList<Action> = mutableListOf()
+ private var authenticationActions: MutableList<AuthenticationAction> = mutableListOf()
+ private var remoteEntry: RemoteEntry? = null
+
+ /**
+ * Sets a remote credential entry to be shown on the UI. Provider must set this if they
+ * wish to get the credential from a different device.
+ *
+ * When constructing the [CredentialEntry] object, the pending intent
+ * must be set such that it leads to an activity that can provide UI to fulfill the request
+ * on a remote device. When user selects this [remoteEntry], the system will
+ * invoke the pending intent set on the [CredentialEntry].
+ *
+ * <p> Once the remote credential flow is complete, the [android.app.Activity]
+ * result should be set to [android.app.Activity#RESULT_OK] and an extra with the
+ * [CredentialProviderService#EXTRA_GET_CREDENTIAL_RESPONSE] key should be populated
+ * with a [android.credentials.Credential] object.
+ *
+ * <p> Note that as a provider service you will only be able to set a remote entry if :
+ * - Provider service possesses the
+ * [android.Manifest.permission.PROVIDE_REMOTE_CREDENTIALS] permission.
+ * - Provider service is configured as the provider that can provide remote entries.
+ *
+ * If the above conditions are not met, setting back [BeginGetCredentialResponse]
+ * on the callback from [CredentialProviderService#onBeginGetCredential] will
+ * throw a [SecurityException].
+ */
+ fun setRemoteEntry(remoteEntry: RemoteEntry?): Builder {
+ this.remoteEntry = remoteEntry
+ return this
+ }
+
+ /**
+ * Adds a [CredentialEntry] to the list of entries to be displayed on the UI.
+ */
+ fun addCredentialEntry(entry: CredentialEntry): Builder {
+ credentialEntries.add(entry)
+ return this
+ }
+
+ /**
+ * Sets the list of credential entries to be displayed on the account selector UI.
+ */
+ fun setCredentialEntries(entries: List<CredentialEntry>): Builder {
+ credentialEntries = entries.toMutableList()
+ return this
+ }
+
+ /**
+ * Adds an [Action] to the list of actions to be displayed on
+ * the UI.
+ *
+ * <p> An [Action] must be used for independent user actions,
+ * such as opening the app, intenting directly into a certain app activity etc. The
+ * pending intent set with the [action] must invoke the corresponding activity.
+ */
+ fun addAction(action: Action): Builder {
+ this.actions.add(action)
+ return this
+ }
+
+ /**
+ * Sets the list of actions to be displayed on the UI.
+ */
+ fun setActions(actions: List<Action>): Builder {
+ this.actions = actions.toMutableList()
+ return this
+ }
+
+ /**
+ * Add an authentication entry to be shown on the UI. Providers must set this entry if
+ * the corresponding account is locked and no underlying credentials can be returned.
+ *
+ * <p> When the user selects this [authenticationAction], the system invokes the
+ * corresponding pending intent.
+ * Once the authentication action activity is launched, and the user is authenticated,
+ * providers should create another response with [BeginGetCredentialResponse] using
+ * this time adding the unlocked credentials in the form of [CredentialEntry]'s.
+ *
+ * <p>The new response object must be set on the authentication activity's
+ * result. The result code should be set to [android.app.Activity#RESULT_OK] and
+ * the [CredentialProviderService#EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE] extra
+ * should be set with the new fully populated [BeginGetCredentialResponse] object.
+ */
+ fun addAuthenticationAction(authenticationAction: AuthenticationAction): Builder {
+ this.authenticationActions.add(authenticationAction)
+ return this
+ }
+
+ /**
+ * Sets the list of authentication entries to be displayed on the account selector UI.
+ */
+ fun setAuthenticationActions(authenticationEntries: List<AuthenticationAction>): Builder {
+ this.authenticationActions = authenticationEntries.toMutableList()
+ return this
+ }
+
+ /**
+ * Builds a [BeginGetCredentialResponse] instance.
+ */
+ fun build(): BeginGetCredentialResponse {
+ return BeginGetCredentialResponse(
+ credentialEntries.toList(),
+ actions.toList(),
+ authenticationActions.toList(),
+ remoteEntry
+ )
+ }
+ }
+
+ @RequiresApi(34)
+ private object Api34Impl {
+ private const val REQUEST_KEY = "androidx.credentials.provider.BeginGetCredentialResponse"
+
+ @JvmStatic
+ @DoNotInline
+ fun writeToBundle(bundle: Bundle, response: BeginGetCredentialResponse) {
+ bundle.putParcelable(
+ REQUEST_KEY,
+ BeginGetCredentialUtil.convertToFrameworkResponse(response)
+ )
+ }
+
+ @JvmStatic
+ @DoNotInline
+ fun readFromBundle(bundle: Bundle): BeginGetCredentialResponse? {
+ val frameworkResponse = bundle.getParcelable(
+ REQUEST_KEY,
+ android.service.credentials.BeginGetCredentialResponse::class.java
+ )
+ if (frameworkResponse != null) {
+ return BeginGetCredentialUtil.convertToJetpackResponse(frameworkResponse)
+ }
+ return null
+ }
+ }
+
+ companion object {
+ /**
+ * Helper method to convert the class to a parcelable [Bundle], in case the class
+ * instance needs to be sent across a process. Consumers of this method should use
+ * [readFromBundle] to reconstruct the class instance back from the bundle returned here.
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun writeToBundle(response: BeginGetCredentialResponse): Bundle {
+ val bundle = Bundle()
+ if (BuildCompat.isAtLeastU()) {
+ Api34Impl.writeToBundle(bundle, response)
+ }
+ return bundle
+ }
+
+ /**
+ * Helper method to convert a [Bundle] retrieved through [writeToBundle], back
+ * to an instance of [BeginGetCredentialResponse].
+ */
+ @JvmStatic
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun readFromBundle(bundle: Bundle): BeginGetCredentialResponse? {
+ if (BuildCompat.isAtLeastU()) {
+ return Api34Impl.readFromBundle(bundle)
+ }
+ return null
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt
new file mode 100644
index 0000000..9c2851d
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCustomCredentialOption.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+
+/**
+ * Allows extending custom versions of BeginGetCredentialOptions for unique use cases.
+ *
+ * If you get a [BeginGetCustomCredentialOption] instead of a type-safe option class such as
+ * [BeginGetPasswordOption], [BeginGetPublicKeyCredentialOption], etc., then you should check if
+ * you have any other library at interest that supports this custom [type] of credential option,
+ * and if so use its parsing utilities to resolve to a type-safe class within that library.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * generated for custom use cases
+ * @property candidateQueryData the partial request data in the [Bundle] format that will be sent to
+ * the provider during the initial candidate query stage, which should not contain sensitive user
+ * information
+ * @throws IllegalArgumentException If [type] is null or, empty
+ */
+open class BeginGetCustomCredentialOption constructor(
+ id: String,
+ type: String,
+ candidateQueryData: Bundle,
+) : BeginGetCredentialOption(
+ id,
+ type,
+ candidateQueryData
+) {
+ init {
+ require(id.isNotEmpty()) { "id should not be empty" }
+ require(type.isNotEmpty()) { "type should not be empty" }
+ }
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ /** @hide */
+ @JvmStatic
+ internal fun createFrom(
+ data: Bundle,
+ id: String,
+ type: String
+ ): BeginGetCustomCredentialOption {
+ return BeginGetCustomCredentialOption(id, type, data)
+ }
+
+ /** @hide */
+ @JvmStatic
+ internal fun createFromEntrySlice(
+ data: Bundle,
+ id: String,
+ type: String
+ ): BeginGetCustomCredentialOption {
+ return BeginGetCustomCredentialOption(id, type, data)
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt
new file mode 100644
index 0000000..80e30a4
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPasswordOption.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import android.service.credentials.BeginGetCredentialResponse
+import androidx.credentials.GetPasswordOption
+import androidx.credentials.PasswordCredential
+
+/**
+ * A request to a password provider to begin the flow of retrieving the user's saved passwords.
+ *
+ * Providers must use the parameters in this option to retrieve the corresponding credentials'
+ * metadata, and then return them in the form of a list of [PasswordCredentialEntry]
+ * set on the [BeginGetCredentialResponse].
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ *
+ * @property allowedUserIds a optional set of user ids with which the credentials associated are
+ * requested; left as empty if the caller app wants to request all the available user credentials
+ */
+class BeginGetPasswordOption constructor(
+ val allowedUserIds: Set<String>,
+ candidateQueryData: Bundle,
+ id: String,
+) : BeginGetCredentialOption(
+ id,
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ candidateQueryData
+) {
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ /** @hide */
+ @JvmStatic
+ internal fun createFrom(data: Bundle, id: String): BeginGetPasswordOption {
+ val allowUserIdList = data.getStringArrayList(
+ GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS)
+ return BeginGetPasswordOption(allowUserIdList?.toSet() ?: emptySet(), data, id)
+ }
+
+ /** @hide */
+ @JvmStatic
+ internal fun createFromEntrySlice(data: Bundle, id: String): BeginGetPasswordOption {
+ val allowUserIdList = data.getStringArrayList(
+ GetPasswordOption.BUNDLE_KEY_ALLOWED_USER_IDS)
+ return BeginGetPasswordOption(allowUserIdList?.toSet() ?: emptySet(), data, id)
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt
new file mode 100644
index 0000000..1ecba47
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetPublicKeyCredentialOption.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.os.Bundle
+import androidx.credentials.GetPublicKeyCredentialOption
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.internal.FrameworkClassParsingException
+
+/**
+ * A request to begin the flow of getting passkeys from the user's public key credential provider.
+ *
+ * @property requestJson the request in JSON format in the standard webauthn web json
+ * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson)
+ * @property clientDataHash a hash that is used to verify the relying party identity, set only if
+ * [android.service.credentials.CallingAppInfo.getOrigin] is set
+ * @param requestJson the request in JSON format in the standard webauthn web json
+ * shown [here](https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson)
+ * @param clientDataHash a hash that is used to verify the relying party identity, set only if
+ * [android.service.credentials.CallingAppInfo.getOrigin] is set
+ * @param id the id of this request option
+ * @param candidateQueryData the request data in the [Bundle] format
+ * @throws NullPointerException If [requestJson] is null
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ */
+class BeginGetPublicKeyCredentialOption @JvmOverloads constructor(
+ candidateQueryData: Bundle,
+ id: String,
+ val requestJson: String,
+ val clientDataHash: ByteArray? = null,
+) : BeginGetCredentialOption(
+ id,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ candidateQueryData
+) {
+ init {
+ require(requestJson.isNotEmpty()) { "requestJson must not be empty" }
+ }
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ /** @hide */
+ @JvmStatic
+ internal fun createFrom(data: Bundle, id: String): BeginGetPublicKeyCredentialOption {
+ try {
+ val requestJson = data.getString(GetPublicKeyCredentialOption
+ .BUNDLE_KEY_REQUEST_JSON)
+ val clientDataHash = data.getByteArray(GetPublicKeyCredentialOption
+ .BUNDLE_KEY_CLIENT_DATA_HASH)
+ return BeginGetPublicKeyCredentialOption(data, id, requestJson!!, clientDataHash)
+ } catch (e: Exception) {
+ throw FrameworkClassParsingException()
+ }
+ }
+
+ /** @hide */
+ @JvmStatic
+ internal fun createFromEntrySlice(data: Bundle, id: String):
+ BeginGetPublicKeyCredentialOption {
+ val requestJson = "dummy_request_json"
+ return BeginGetPublicKeyCredentialOption(data, id, requestJson)
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
new file mode 100644
index 0000000..f9a561a
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.credentials.CredentialManager
+import androidx.credentials.PasswordCredential
+import androidx.credentials.PublicKeyCredential
+import java.time.Instant
+import java.util.Collections
+
+/**
+ * An entry to be shown on the selector during a create flow initiated when an app calls
+ * [CredentialManager.createCredential]
+ *
+ * A [CreateEntry] points to a location such as an account, or a group where the credential can be
+ * registered. When user selects this entry, the corresponding [PendingIntent] is fired, and the
+ * credential creation can be completed.
+ *
+ * @throws IllegalArgumentException If [accountName] is empty
+ */
+@RequiresApi(28)
+class CreateEntry internal constructor(
+ val accountName: CharSequence,
+ val pendingIntent: PendingIntent,
+ val icon: Icon?,
+ val description: CharSequence?,
+ val lastUsedTime: Instant?,
+ private val credentialCountInformationMap: MutableMap<String, Int?>,
+ val isAutoSelectAllowed: Boolean
+) {
+
+ /**
+ * Creates an entry to be displayed on the selector during create flows.
+ *
+ * @param accountName the name of the account where the credential will be saved
+ * @param pendingIntent the [PendingIntent] that will get invoked when user selects this entry
+ * @param description the localized description shown on UI about where the credential is stored
+ * @param icon the icon to be displayed with this entry on the UI
+ * @param lastUsedTime the last time the account underlying this entry was used by the user
+ * @param passwordCredentialCount the no. of password credentials saved by the provider
+ * @param publicKeyCredentialCount the no. of public key credentials saved by the provider
+ * @param totalCredentialCount the total no. of credentials saved by the provider
+ * @param isAutoSelectAllowed whether the entry should be auto selected if it is the only
+ * entry on the selector
+ *
+ * @throws IllegalArgumentException If [accountName] is empty, or if [description] is longer
+ * than 300 characters (important: make sure your descriptions across all locales are within
+ * this limit)
+ * @throws NullPointerException If [accountName] or [pendingIntent] is null
+ */
+ constructor(
+ accountName: CharSequence,
+ pendingIntent: PendingIntent,
+ description: CharSequence? = null,
+ lastUsedTime: Instant? = null,
+ icon: Icon? = null,
+ @Suppress("AutoBoxing")
+ passwordCredentialCount: Int? = null,
+ @Suppress("AutoBoxing")
+ publicKeyCredentialCount: Int? = null,
+ @Suppress("AutoBoxing")
+ totalCredentialCount: Int? = null,
+ isAutoSelectAllowed: Boolean = false
+ ) : this(
+ accountName,
+ pendingIntent,
+ icon,
+ description,
+ lastUsedTime,
+ mutableMapOf(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL to passwordCredentialCount,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL to publicKeyCredentialCount,
+ TYPE_TOTAL_CREDENTIAL to totalCredentialCount
+ ),
+ isAutoSelectAllowed
+ )
+
+ init {
+ require(accountName.isNotEmpty()) { "accountName must not be empty" }
+ if (description != null) {
+ require(description.length <= DESCRIPTION_MAX_CHAR_LIMIT) {
+ "Description must follow a limit of 300 characters."
+ }
+ }
+ }
+
+ /** Returns the no. of password type credentials that the provider with this entry has. */
+ @Suppress("AutoBoxing")
+ fun getPasswordCredentialCount(): Int? {
+ return credentialCountInformationMap[PasswordCredential.TYPE_PASSWORD_CREDENTIAL]
+ }
+
+ /** Returns the no. of public key type credentials that the provider with this entry has. */
+ @Suppress("AutoBoxing")
+ fun getPublicKeyCredentialCount(): Int? {
+ return credentialCountInformationMap[PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL]
+ }
+
+ /** Returns the no. of total credentials that the provider with this entry has.
+ *
+ * This total count is not necessarily equal to the sum of [getPasswordCredentialCount]
+ * and [getPublicKeyCredentialCount].
+ *
+ */
+ @Suppress("AutoBoxing")
+ fun getTotalCredentialCount(): Int? {
+ return credentialCountInformationMap[TYPE_TOTAL_CREDENTIAL]
+ }
+
+ /**
+ * A builder for [CreateEntry]
+ *
+ * @param accountName the name of the account where the credential will be registered
+ * @param pendingIntent the [PendingIntent] that will be fired when the user selects
+ * this entry
+ */
+ class Builder constructor(
+ private val accountName: CharSequence,
+ private val pendingIntent: PendingIntent
+ ) {
+
+ private var credentialCountInformationMap: MutableMap<String, Int?> =
+ mutableMapOf()
+ private var icon: Icon? = null
+ private var description: CharSequence? = null
+ private var lastUsedTime: Instant? = null
+ private var passwordCredentialCount: Int? = null
+ private var publicKeyCredentialCount: Int? = null
+ private var totalCredentialCount: Int? = null
+ private var autoSelectAllowed: Boolean = false
+
+ /**
+ * Sets whether the entry should be auto-selected.
+ * The value is false by default.
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
+ this.autoSelectAllowed = autoSelectAllowed
+ return this
+ }
+
+ /** Sets the password credential count, denoting how many credentials of type
+ * [PasswordCredential.TYPE_PASSWORD_CREDENTIAL] does the provider have stored.
+ *
+ * This information will be displayed on the [CreateEntry] to help the user
+ * make a choice.
+ */
+ fun setPasswordCredentialCount(count: Int): Builder {
+ passwordCredentialCount = count
+ credentialCountInformationMap[PasswordCredential.TYPE_PASSWORD_CREDENTIAL] = count
+ return this
+ }
+
+ /** Sets the password credential count, denoting how many credentials of type
+ * [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL] does the provider have stored.
+ *
+ * This information will be displayed on the [CreateEntry] to help the user
+ * make a choice.
+ */
+ fun setPublicKeyCredentialCount(count: Int): Builder {
+ publicKeyCredentialCount = count
+ credentialCountInformationMap[PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL] = count
+ return this
+ }
+
+ /** Sets the total credential count, denoting how many credentials in total
+ * does the provider have stored.
+ *
+ * This total count no. does not need to be a total of the counts set through
+ * [setPasswordCredentialCount] and [setPublicKeyCredentialCount].
+ *
+ * This information will be displayed on the [CreateEntry] to help the user
+ * make a choice.
+ */
+ fun setTotalCredentialCount(count: Int): Builder {
+ totalCredentialCount = count
+ credentialCountInformationMap[TYPE_TOTAL_CREDENTIAL] = count
+ return this
+ }
+
+ /** Sets an icon to be displayed with the entry on the UI */
+ fun setIcon(icon: Icon?): Builder {
+ this.icon = icon
+ return this
+ }
+
+ /**
+ * Sets a localized description to be displayed on the UI at the time of credential
+ * creation.
+ *
+ * Typically this description should contain information informing the user of the
+ * credential being created, and where it is being stored. Providers are free
+ * to phrase this however they see fit.
+ *
+ * @throws IllegalArgumentException if [description] is longer than 300 characters (
+ * important: make sure your descriptions across all locales are within this limit).
+ */
+ fun setDescription(description: CharSequence?): Builder {
+ if (description?.length != null && description.length > DESCRIPTION_MAX_CHAR_LIMIT) {
+ throw IllegalArgumentException("Description must follow a limit of 300 characters.")
+ }
+ this.description = description
+ return this
+ }
+
+ /** Sets the last time this account was used */
+ fun setLastUsedTime(lastUsedTime: Instant?): Builder {
+ this.lastUsedTime = lastUsedTime
+ return this
+ }
+
+ /**
+ * Builds an instance of [CreateEntry]
+ *
+ * @throws IllegalArgumentException If [accountName] is empty
+ */
+ fun build(): CreateEntry {
+ return CreateEntry(
+ accountName, pendingIntent, icon, description, lastUsedTime,
+ credentialCountInformationMap, autoSelectAllowed
+ )
+ }
+ }
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "CreateEntry"
+ private const val DESCRIPTION_MAX_CHAR_LIMIT = 300
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val TYPE_TOTAL_CREDENTIAL = "TOTAL_CREDENTIAL_COUNT_TYPE"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_ACCOUNT_NAME =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_NOTE =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_NOTE"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
+
+ private const val SLICE_HINT_AUTO_SELECT_ALLOWED =
+ "androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_SELECT_ALLOWED"
+
+ private const val AUTO_SELECT_TRUE_STRING = "true"
+
+ private const val AUTO_SELECT_FALSE_STRING = "false"
+
+ /** @hide **/
+ @JvmStatic
+ fun toSlice(
+ createEntry: CreateEntry
+ ): Slice {
+ val accountName = createEntry.accountName
+ val icon = createEntry.icon
+ val description = createEntry.description
+ val lastUsedTime = createEntry.lastUsedTime
+ val credentialCountInformationMap = createEntry.credentialCountInformationMap
+ val pendingIntent = createEntry.pendingIntent
+
+ // TODO("Use the right type and revision")
+ val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
+
+ val autoSelectAllowed = if (createEntry.isAutoSelectAllowed) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+
+ sliceBuilder.addText(
+ accountName, /*subType=*/null,
+ listOf(SLICE_HINT_ACCOUNT_NAME)
+ )
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(), /*subType=*/null, listOf(
+ SLICE_HINT_LAST_USED_TIME_MILLIS
+ )
+ )
+ }
+ if (description != null) {
+ sliceBuilder.addText(
+ description, null,
+ listOf(SLICE_HINT_NOTE)
+ )
+ }
+ if (icon != null) {
+ sliceBuilder.addIcon(
+ icon, /*subType=*/null,
+ listOf(SLICE_HINT_ICON)
+ )
+ }
+ val credentialCountBundle = convertCredentialCountInfoToBundle(
+ credentialCountInformationMap
+ )
+ if (credentialCountBundle != null) {
+ sliceBuilder.addBundle(
+ convertCredentialCountInfoToBundle(
+ credentialCountInformationMap
+ ), null, listOf(
+ SLICE_HINT_CREDENTIAL_COUNT_INFORMATION
+ )
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ ).addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_SELECT_ALLOWED)
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [CreateEntry] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @RequiresApi(28)
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): CreateEntry? {
+ // TODO("Put the right spec and version value")
+ var accountName: CharSequence? = null
+ var icon: Icon? = null
+ var pendingIntent: PendingIntent? = null
+ var credentialCountInfo: MutableMap<String, Int?> = mutableMapOf()
+ var description: CharSequence? = null
+ var lastUsedTime: Instant? = null
+ var autoSelectAllowed = false
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
+ accountName = it.text
+ } else if (it.hasHint(SLICE_HINT_ICON)) {
+ icon = it.icon
+ } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {
+ credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)
+ as MutableMap<String, Int?>
+ } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTime = Instant.ofEpochMilli(it.long)
+ } else if (it.hasHint(SLICE_HINT_NOTE)) {
+ description = it.text
+ } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTime = Instant.ofEpochMilli(it.long)
+ } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_ALLOWED)) {
+ val autoSelectValue = it.text
+ if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
+ autoSelectAllowed = true
+ }
+ }
+ }
+ return try {
+ CreateEntry(
+ accountName!!, pendingIntent!!, icon, description,
+ lastUsedTime, credentialCountInfo, autoSelectAllowed
+ )
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+
+ /** @hide **/
+ @JvmStatic
+ internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):
+ Map<String, Int?> {
+ val credentialCountMap = HashMap<String, Int?>()
+ if (bundle == null) {
+ return credentialCountMap
+ }
+ bundle.keySet().forEach {
+ try {
+ credentialCountMap[it] = bundle.getInt(it)
+ } catch (e: Exception) {
+ Log.i(TAG, "Issue unpacking credential count info bundle: " + e.message)
+ }
+ }
+ return credentialCountMap
+ }
+
+ /** @hide **/
+ @JvmStatic
+ internal fun convertCredentialCountInfoToBundle(
+ credentialCountInformationMap: Map<String, Int?>
+ ): Bundle? {
+ var foundCredentialCount = false
+ val bundle = Bundle()
+ credentialCountInformationMap.forEach {
+ if (it.value != null) {
+ bundle.putInt(it.key, it.value!!)
+ foundCredentialCount = true
+ }
+ }
+ if (!foundCredentialCount) {
+ return null
+ }
+ return bundle
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
new file mode 100644
index 0000000..4f020ced
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.app.slice.Slice
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.credentials.PasswordCredential.Companion.TYPE_PASSWORD_CREDENTIAL
+import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+
+/**
+ * Base class for a credential entry to be displayed on
+ * the selector.
+ */
+abstract class CredentialEntry internal constructor(
+ /** @hide */
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ open val type: String,
+ val beginGetCredentialOption: BeginGetCredentialOption,
+ /** @hide */
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ val slice: Slice
+) {
+ /** @hide **/
+ companion object {
+ @JvmStatic
+ @RequiresApi(34)
+ internal fun createFrom(slice: Slice): CredentialEntry? {
+ return try {
+ when (slice.spec?.type) {
+ TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(slice)!!
+ TYPE_PUBLIC_KEY_CREDENTIAL -> PublicKeyCredentialEntry.fromSlice(slice)!!
+ else -> CustomCredentialEntry.fromSlice(slice)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ CustomCredentialEntry.fromSlice(slice)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialProviderService.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialProviderService.kt
new file mode 100644
index 0000000..27406d7
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialProviderService.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.credentials.ClearCredentialStateException
+import android.credentials.GetCredentialException
+import android.os.CancellationSignal
+import android.os.OutcomeReceiver
+import android.service.credentials.ClearCredentialStateRequest
+import android.service.credentials.CredentialEntry
+import android.service.credentials.CredentialProviderService
+import androidx.annotation.RequiresApi
+import androidx.credentials.exceptions.ClearCredentialException
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.provider.utils.BeginCreateCredentialUtil
+import androidx.credentials.provider.utils.BeginGetCredentialUtil
+import androidx.credentials.provider.utils.ClearCredentialUtil
+
+/**
+ * A [CredentialProviderService] is a service used to save and retrieve credentials for a given
+ * user, upon the request of a client app that typically uses these credentials for sign-in flows.
+ *
+ * The credential retrieval and creation/saving is mediated by the Android System that
+ * aggregates credentials from multiple credential provider services, and presents them to
+ * the user in the form of a selector UI for credential selections/account selections/
+ * confirmations etc.
+ *
+ * A [CredentialProviderService] is only bound to the Android System for the span
+ * of a [androidx.credentials.CredentialManager] get/create API call. The service is bound only
+ * if :
+ * 1. The service requires the [android.Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE]
+ * permission.
+ * 2. The user has enabled this service as a credential provider from the
+ * settings.
+ *
+ * ## Basic Usage
+ * The basic Credential Manager flow is as such:
+ * - Client app calls one of the APIs exposed in [androidx.credentials.CredentialManager].
+ * - Android system propagates the developer's request to providers that have been
+ * enabled by the user, and can support the [androidx.credentials.Credential] type
+ * specified in the request. We call this the **query phase** of provider communication.
+ * Developer may specify a different set of request parameters to be sent to the provider
+ * during this phase.
+ * - In this query phase, providers, in most cases, will respond with a list of
+ * [CredentialEntry], and an optional list of [Action] entries (for the get flow), and a list
+ * of [CreateEntry] (for the create flow). No actual credentials will be returned in this phase.
+ * - Provider responses are aggregated and presented to the user in the form of a selector UI.
+ * - User selects an entry on the selector.
+ * - Android System invokes the [PendingIntent] associated with this entry, that belongs to the
+ * corresponding provider. We call this the **final phase** of provider communication. The
+ * [PendingIntent] contains the complete request originally created by the developer.
+ * - Provider finishes the [Activity] invoked by the [PendingIntent] by setting the result
+ * as the activity is finished.
+ * - Android System sends back the result to the client app.
+ *
+ * The flow described above minimizes the amount of time a service is bound to the system.
+ * Calls to the service are considered stateless. If a service wishes to maintain state
+ * between the calls, it must do its own state management.
+ * Note: The service's process might be killed by the Android System when unbound, for cases
+ * such as low memory on the device.
+ *
+ * ## Service Registration
+ * In order for Credential Manager to propagate requests to a given provider service, the provider
+ * must:
+ * - Extend this class and implement the abstract methods.
+ * - Declare the [CredentialProviderService.SERVICE_INTERFACE] intent as handled by the service.
+ * - Require the [android.Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE] permission.
+ * - Declare capabilities that the provider supports. Capabilities are essentially credential types
+ * that the provider can handle. Capabilities must be added to the metadata of the service against
+ * [CredentialProviderService.SERVICE_META_DATA].
+ */
+@RequiresApi(34)
+abstract class CredentialProviderService : CredentialProviderService() {
+
+ final override fun onBeginGetCredential(
+ request: android.service.credentials.BeginGetCredentialRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<
+ android.service.credentials.BeginGetCredentialResponse, GetCredentialException>
+ ) {
+ val structuredRequest = BeginGetCredentialUtil.convertToJetpackRequest(request)
+ val outcome = object : OutcomeReceiver<BeginGetCredentialResponse,
+ androidx.credentials.exceptions.GetCredentialException> {
+ override fun onResult(response: BeginGetCredentialResponse) {
+ callback.onResult(
+ BeginGetCredentialUtil
+ .convertToFrameworkResponse(response)
+ )
+ }
+
+ override fun onError(error: androidx.credentials.exceptions.GetCredentialException) {
+ super.onError(error)
+ // TODO("Change error code to provider error when ready on framework")
+ callback.onError(GetCredentialException(error.type, error.message))
+ }
+ }
+ this.onBeginGetCredentialRequest(structuredRequest, cancellationSignal, outcome)
+ }
+
+ final override fun onBeginCreateCredential(
+ request: android.service.credentials.BeginCreateCredentialRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,
+ android.credentials.CreateCredentialException>
+ ) {
+ val outcome = object : OutcomeReceiver<
+ BeginCreateCredentialResponse, CreateCredentialException> {
+ override fun onResult(response: BeginCreateCredentialResponse) {
+ callback.onResult(
+ BeginCreateCredentialUtil
+ .convertToFrameworkResponse(response)
+ )
+ }
+
+ override fun onError(error: CreateCredentialException) {
+ super.onError(error)
+ // TODO("Change error code to provider error when ready on framework")
+ callback.onError(
+ android.credentials.CreateCredentialException(
+ error.type, error.message
+ )
+ )
+ }
+ }
+ onBeginCreateCredentialRequest(
+ BeginCreateCredentialUtil.convertToJetpackRequest(request),
+ cancellationSignal, outcome
+ )
+ }
+
+ final override fun onClearCredentialState(
+ request: ClearCredentialStateRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<Void, ClearCredentialStateException>
+ ) {
+ val outcome = object : OutcomeReceiver<Void?, ClearCredentialException> {
+ override fun onResult(response: Void?) {
+ callback.onResult(response)
+ }
+
+ override fun onError(error: ClearCredentialException) {
+ super.onError(error)
+ // TODO("Change error code to provider error when ready on framework")
+ callback.onError(ClearCredentialStateException(error.type, error.message))
+ }
+ }
+ onClearCredentialStateRequest(ClearCredentialUtil.convertToJetpackRequest(request),
+ cancellationSignal, outcome)
+ }
+
+ /**
+ * Called by the Android System in response to a client app calling
+ * [androidx.credentials.CredentialManager.clearCredentialState]. A client app typically
+ * calls this API on instances like sign-out when the intention is that the providers clear
+ * any state that they may have maintained for the given user.
+ *
+ * You should invoked this api after your user signs out of your app to notify all credential
+ * providers that any stored credential session for the given app should be cleared.
+ *
+ * An example scenario of a state that is maintained and is expected to be cleared on this
+ * call, is when an active credential session is being stored to limit sign-in options
+ * in the result of subsequent get-request calls. When a user explicitly signs out of the app,
+ * the next time, the client app may want their users to see all options and hence will call
+ * this API first to make sure credential providers can clear the state maintained previously.
+ *
+ * @param request the request for the credential provider to handle
+ * @param cancellationSignal signal for observing cancellation requests. The system will
+ * use this to notify you that the result is no longer needed and you should stop
+ * handling it in order to save your resources
+ * @param callback the callback object to be used to notify the response or error
+ */
+ abstract fun onClearCredentialStateRequest(
+ request: ProviderClearCredentialStateRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<Void?,
+ ClearCredentialException>
+ )
+
+ /**
+ * Called by the Android System in response to a client app calling
+ * [androidx.credentials.CredentialManager.getCredential], to get a credential
+ * sourced from a credential provider installed on the device.
+ *
+ * Credential provider services must extend this method in order to handle a
+ * [BeginGetCredentialRequest] request. Once processed, the service must call one of the
+ * [callback] methods to notify the result of the request.
+ *
+ * This API call is referred to as the **query phase** of the original get request from
+ * the client app. In this phase, provider must go over all the
+ * [android.service.credentials.BeginGetCredentialOption], and add corresponding a
+ * [CredentialEntry] to the [BeginGetCredentialResponse]. Each [CredentialEntry] should
+ * contain meta-data to be shown on the selector UI. In addition, each [CredentialEntry]
+ * must contain a [PendingIntent].
+ * Optionally, providers can also add [Action] entries for any non-credential related actions
+ * that they want to offer to the users e.g. opening app, managing credentials etc.
+ *
+ * When user selects one of the [CredentialEntry], **final phase** of the original client's
+ * get-request flow starts. The Android System attached the complete
+ * [androidx.credentials.provider.ProviderGetCredentialRequest] to an intent extra of the
+ * activity that is started by the pending intent. The request must be retrieved through
+ * [PendingIntentHandler.retrieveProviderGetCredentialRequest]. This final request
+ * will only contain a single [androidx.credentials.CredentialOption] that contains the
+ * parameters of the credential the user has requested. The provider service must retrieve this
+ * credential and return through [PendingIntentHandler.setGetCredentialResponse].
+ *
+ * **Handling locked provider apps**
+ * If the provider app is locked, and the provider cannot provide any meta-data based
+ * [CredentialEntry], provider must set an [AuthenticationAction] on the
+ * [BeginGetCredentialResponse]. The [PendingIntent] set on this entry must lead the user
+ * to an >unlock activity. Once unlocked, the provider must retrieve all credentials,
+ * and set the list of [CredentialEntry] and the list of optional [Action] as a result
+ * of the >unlock activity through [PendingIntentHandler.setBeginGetCredentialResponse].
+ *
+ * @see CredentialEntry for how an entry representing a credential must be built
+ * @see Action for how a non-credential related action should be built
+ * @see AuthenticationAction for how an entry that navigates the user to an unlock flow
+ * can be built
+ *
+ * @param [request] the [ProviderGetCredentialRequest] to handle
+ * See [BeginGetCredentialResponse] for the response to be returned
+ * @param cancellationSignal signal for observing cancellation requests. The system will
+ * use this to notify you that the result is no longer needed and you should stop
+ * handling it in order to save your resources
+ * @param callback the callback object to be used to notify the response or error
+ */
+ abstract fun onBeginGetCredentialRequest(
+ request: BeginGetCredentialRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<BeginGetCredentialResponse,
+ androidx.credentials.exceptions.GetCredentialException>
+ )
+
+ /**
+ * Called by the Android System in response to a client app calling
+ * [androidx.credentials.CredentialManager.createCredential], to create/save a credential
+ * with a credential provider installed on the device.
+ *
+ * Credential provider services must extend this method in order to handle a
+ * [BeginCreateCredentialRequest] request. Once processed, the service must call one of the
+ * [callback] methods to notify the result of the request.
+ *
+ * This API call is referred to as the **query phase** of the original create request from
+ * the client app. In this phase, provider must process the request parameters in the
+ * [BeginCreateCredentialRequest] and return a list of [CreateEntry] whereby every
+ * entry represents an account/group where the user will be storing the credential. Each
+ * [CreateEntry] must contain a [PendingIntent] that will lead the user to an activity
+ * in the credential provider's app that will complete the actual credential creation.
+ *
+ * When user selects one of the [CreateEntry], the associated [PendingIntent] will be invoked
+ * and the provider will receive the complete request as part of the extras in the resulting
+ * activity. Provider must retrieve the request through
+ * [PendingIntentHandler.retrieveProviderCreateCredentialRequest].
+ * Once the activity is complete, and the credential is created, provider must set back the
+ * response through [PendingIntentHandler.setCreateCredentialResponse].
+ *
+ * @param [request] the [BeginCreateCredentialRequest] to handle
+ * See [BeginCreateCredentialResponse] for the response to be returned
+ * @param cancellationSignal signal for observing cancellation requests. The system will
+ * use this to notify you that the result is no longer needed and you should stop
+ * handling it in order to save your resources
+ * @param callback the callback object to be used to notify the response or error
+ */
+ abstract fun onBeginCreateCredentialRequest(
+ request: BeginCreateCredentialRequest,
+ cancellationSignal: CancellationSignal,
+ callback: OutcomeReceiver<BeginCreateCredentialResponse,
+ CreateCredentialException>
+ )
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
new file mode 100644
index 0000000..75fc027
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.credentials.CredentialOption
+import androidx.credentials.R
+import java.time.Instant
+import java.util.Collections
+
+/**
+ * Custom credential entry for a custom credential tyoe that is displayed on the account
+ * selector UI.
+ *
+ * Each entry corresponds to an account that can provide a credential.
+ *
+ * @property title the title shown with this entry on the selector UI
+ * @property subtitle the subTitle shown with this entry on the selector UI
+ * @property lastUsedTime the last used time the credential underlying this entry was
+ * used by the user
+ * @property icon the icon to be displayed with this entry on the selector UI. If not set, a
+ * default icon representing a custom credential type is set by the library
+ * @property pendingIntent the [PendingIntent] to be invoked when this entry
+ * is selected by the user
+ * @property typeDisplayName the friendly name to be displayed on the UI for
+ * the type of the credential
+ * @property isAutoSelectAllowed whether this entry is allowed to be auto
+ * selected if it is the only one on the UI. Note that setting this value
+ * to true does not guarantee this behavior. The developer must also set this
+ * to true, and the framework must determine that only one entry is present
+ */
+@RequiresApi(28)
+class CustomCredentialEntry internal constructor(
+ override val type: String,
+ val title: CharSequence,
+ val pendingIntent: PendingIntent,
+ @get:Suppress("AutoBoxing")
+ val isAutoSelectAllowed: Boolean,
+ val subtitle: CharSequence?,
+ val typeDisplayName: CharSequence?,
+ val icon: Icon,
+ val lastUsedTime: Instant?,
+ beginGetCredentialOption: BeginGetCredentialOption,
+ /** @hide */
+ val autoSelectAllowedFromOption: Boolean = false,
+ /** @hide */
+ val isDefaultIcon: Boolean = false
+) : CredentialEntry(
+ type,
+ beginGetCredentialOption,
+ toSlice(
+ type,
+ title,
+ subtitle,
+ pendingIntent,
+ typeDisplayName,
+ lastUsedTime,
+ icon,
+ isAutoSelectAllowed,
+ beginGetCredentialOption
+ )
+) {
+ init {
+ require(type.isNotEmpty()) { "type must not be empty" }
+ require(title.isNotEmpty()) { "title must not be empty" }
+ }
+
+ constructor(
+ context: Context,
+ title: CharSequence,
+ pendingIntent: PendingIntent,
+ beginGetCredentialOption: BeginGetCredentialOption,
+ subtitle: CharSequence? = null,
+ typeDisplayName: CharSequence? = null,
+ lastUsedTime: Instant? = null,
+ icon: Icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in),
+ @Suppress("AutoBoxing")
+ isAutoSelectAllowed: Boolean = false
+ ) : this(
+ beginGetCredentialOption.type,
+ title,
+ pendingIntent,
+ isAutoSelectAllowed,
+ subtitle,
+ typeDisplayName,
+ icon,
+ lastUsedTime,
+ beginGetCredentialOption
+ )
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "CredentialEntry"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_ALLOWED =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_OPTION_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_DEFAULT_ICON_RES_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_TRUE_STRING = "true"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_FALSE_STRING = "false"
+
+ /** @hide */
+ @JvmStatic
+ fun toSlice(
+ type: String,
+ title: CharSequence,
+ subtitle: CharSequence?,
+ pendingIntent: PendingIntent,
+ typeDisplayName: CharSequence?,
+ lastUsedTime: Instant?,
+ icon: Icon,
+ isAutoSelectAllowed: Boolean?,
+ beginGetCredentialOption: BeginGetCredentialOption
+ ): Slice {
+ // TODO("Put the right revision value")
+ val autoSelectAllowed = if (isAutoSelectAllowed == true) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ type, 1
+ )
+ )
+ .addText(
+ typeDisplayName, /*subType=*/null,
+ listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(SLICE_HINT_TITLE)
+ )
+ .addText(
+ subtitle, /*subType=*/null,
+ listOf(SLICE_HINT_SUBTITLE)
+ )
+ .addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_ALLOWED)
+ )
+ .addText(
+ beginGetCredentialOption.id,
+ /*subType=*/null,
+ listOf(SLICE_HINT_OPTION_ID)
+ )
+ .addIcon(
+ icon, /*subType=*/null,
+ listOf(SLICE_HINT_ICON)
+ )
+
+ try {
+ if (icon.resId == R.drawable.ic_other_sign_in) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_DEFAULT_ICON_RES_ID)
+ )
+ }
+ } catch (_: IllegalStateException) {
+ }
+
+ if (CredentialOption.extractAutoSelectValue(
+ beginGetCredentialOption.candidateQueryData
+ )
+ ) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_SELECT_FROM_OPTION)
+ )
+ }
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(),
+ /*subType=*/null,
+ listOf(SLICE_HINT_LAST_USED_TIME_MILLIS)
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): CustomCredentialEntry? {
+ val type: String = slice.spec!!.type
+ var typeDisplayName: CharSequence? = null
+ var title: CharSequence? = null
+ var subtitle: CharSequence? = null
+ var icon: Icon? = null
+ var pendingIntent: PendingIntent? = null
+ var lastUsedTime: Instant? = null
+ var autoSelectAllowed = false
+ var beginGetCredentialOptionId: CharSequence? = null
+ var autoSelectAllowedFromOption = false
+ var isDefaultIcon = false
+
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+ typeDisplayName = it.text
+ } else if (it.hasHint(SLICE_HINT_TITLE)) {
+ title = it.text
+ } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
+ subtitle = it.text
+ } else if (it.hasHint(SLICE_HINT_ICON)) {
+ icon = it.icon
+ } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+ beginGetCredentialOptionId = it.text
+ } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTime = Instant.ofEpochMilli(it.long)
+ } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
+ val autoSelectValue = it.text
+ if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
+ autoSelectAllowed = true
+ }
+ } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
+ autoSelectAllowedFromOption = true
+ } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
+ isDefaultIcon = true
+ }
+ }
+
+ return try {
+ CustomCredentialEntry(
+ type,
+ title!!,
+ pendingIntent!!,
+ autoSelectAllowed,
+ subtitle,
+ typeDisplayName,
+ icon!!,
+ lastUsedTime,
+ BeginGetCustomCredentialOption(
+ beginGetCredentialOptionId!!.toString(),
+ type,
+ Bundle()
+ ),
+ autoSelectAllowedFromOption,
+ isDefaultIcon
+ )
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+
+ /** Builder for [CustomCredentialEntry] */
+ class Builder(
+ private val context: Context,
+ private val type: String,
+ private val title: CharSequence,
+ private val pendingIntent: PendingIntent,
+ private val beginGetCredentialOption: BeginGetCredentialOption
+ ) {
+ private var subtitle: CharSequence? = null
+ private var lastUsedTime: Instant? = null
+ private var typeDisplayName: CharSequence? = null
+ private var icon: Icon? = null
+ private var autoSelectAllowed = false
+
+ /** Sets a displayName to be shown on the UI with this entry. */
+ fun setSubtitle(subtitle: CharSequence?): Builder {
+ this.subtitle = subtitle
+ return this
+ }
+
+ /** Sets the display name of this credential type, to be shown on the UI with this entry. */
+ fun setTypeDisplayName(typeDisplayName: CharSequence?): Builder {
+ this.typeDisplayName = typeDisplayName
+ return this
+ }
+
+ /**
+ * Sets the icon to be show on the UI.
+ * If no icon is set, a default icon representing a custom credential will be set.
+ */
+ fun setIcon(icon: Icon): Builder {
+ this.icon = icon
+ return this
+ }
+
+ /**
+ * Sets whether the entry should be auto-selected.
+ * The value is false by default
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
+ this.autoSelectAllowed = autoSelectAllowed
+ return this
+ }
+
+ /**
+ * Sets the last used time of this account. This information will be used to sort the
+ * entries on the selector.
+ */
+ fun setLastUsedTime(lastUsedTime: Instant?): Builder {
+ this.lastUsedTime = lastUsedTime
+ return this
+ }
+
+ /** Builds an instance of [CustomCredentialEntry] */
+ fun build(): CustomCredentialEntry {
+ if (icon == null) {
+ icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in)
+ }
+ return CustomCredentialEntry(
+ type,
+ title,
+ pendingIntent,
+ autoSelectAllowed,
+ subtitle,
+ typeDisplayName,
+ icon!!,
+ lastUsedTime,
+ beginGetCredentialOption
+ )
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
new file mode 100644
index 0000000..117e783
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.credentials.CredentialOption
+import androidx.credentials.PasswordCredential
+import androidx.credentials.R
+import java.time.Instant
+import java.util.Collections
+
+/**
+ * A password credential entry that is displayed on the account selector UI. This
+ * entry denotes that a credential of type [PasswordCredential.TYPE_PASSWORD_CREDENTIAL]
+ * is available for the user to select.
+ *
+ * Once this entry is selected, the corresponding [pendingIntent] will be invoked. The provider
+ * can then show any activity they wish to. Before finishing the activity, provider must
+ * set the final [androidx.credentials.GetCredentialResponse] through the
+ * [PendingIntentHandler.setGetCredentialResponse] helper API.
+ *
+ * @property username the username of the account holding the password credential
+ * @property displayName the displayName of the account holding the password credential
+ * @property lastUsedTime the last used time of this entry
+ * @property icon the icon to be displayed with this entry on the selector. If not set, a
+ * default icon representing a password credential type is set by the library
+ * @property pendingIntent the [PendingIntent] to be invoked when user selects
+ * this entry
+ * @property isAutoSelectAllowed whether this entry is allowed to be auto
+ * selected if it is the only one on the UI. Note that setting this value
+ * to true does not guarantee this behavior. The developer must also set this
+ * to true, and the framework must determine that this is the only entry available for the user.
+ *
+ * @throws IllegalArgumentException if [username] is empty
+ *
+ * @see CustomCredentialEntry
+ */
+@RequiresApi(28)
+class PasswordCredentialEntry internal constructor(
+ val username: CharSequence,
+ val displayName: CharSequence?,
+ val typeDisplayName: CharSequence,
+ val pendingIntent: PendingIntent,
+ val lastUsedTime: Instant?,
+ val icon: Icon,
+ val isAutoSelectAllowed: Boolean,
+ beginGetPasswordOption: BeginGetPasswordOption,
+ /** @hide */
+ val autoSelectAllowedFromOption: Boolean = false,
+ /** @hide */
+ val isDefaultIcon: Boolean = false
+) : CredentialEntry(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ beginGetPasswordOption,
+ toSlice(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ username,
+ displayName,
+ pendingIntent,
+ typeDisplayName,
+ lastUsedTime,
+ icon,
+ isAutoSelectAllowed,
+ beginGetPasswordOption
+ )
+) {
+ init {
+ require(username.isNotEmpty()) { "username must not be empty" }
+ }
+
+ constructor(
+ context: Context,
+ username: CharSequence,
+ pendingIntent: PendingIntent,
+ beginGetPasswordOption: BeginGetPasswordOption,
+ displayName: CharSequence? = null,
+ lastUsedTime: Instant? = null,
+ icon: Icon = Icon.createWithResource(context, R.drawable.ic_password),
+ isAutoSelectAllowed: Boolean = false
+ ) : this(
+ username,
+ displayName,
+ typeDisplayName = context.getString(
+ R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL
+ ),
+ pendingIntent,
+ lastUsedTime,
+ icon,
+ isAutoSelectAllowed,
+ beginGetPasswordOption,
+ )
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "PasswordCredentialEntry"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_DEFAULT_ICON_RES_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_OPTION_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_ALLOWED =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_TRUE_STRING = "true"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_FALSE_STRING = "false"
+
+ /** @hide */
+ @JvmStatic
+ fun toSlice(
+ type: String,
+ title: CharSequence,
+ subTitle: CharSequence?,
+ pendingIntent: PendingIntent,
+ typeDisplayName: CharSequence?,
+ lastUsedTime: Instant?,
+ icon: Icon,
+ isAutoSelectAllowed: Boolean,
+ beginGetPasswordCredentialOption: BeginGetPasswordOption
+ ): Slice {
+ // TODO("Put the right revision value")
+ val autoSelectAllowed = if (isAutoSelectAllowed) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ type, 1
+ )
+ )
+ .addText(
+ typeDisplayName, /*subType=*/null,
+ listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(SLICE_HINT_TITLE)
+ )
+ .addText(
+ subTitle, /*subType=*/null,
+ listOf(SLICE_HINT_SUBTITLE)
+ )
+ .addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_ALLOWED)
+ )
+ .addText(
+ beginGetPasswordCredentialOption.id,
+ /*subType=*/null,
+ listOf(SLICE_HINT_OPTION_ID)
+ )
+ .addIcon(
+ icon, /*subType=*/null,
+ listOf(SLICE_HINT_ICON)
+ )
+ try {
+ if (icon.resId == R.drawable.ic_password) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_DEFAULT_ICON_RES_ID)
+ )
+ }
+ } catch (_: IllegalStateException) {
+ }
+
+ if (CredentialOption.extractAutoSelectValue(
+ beginGetPasswordCredentialOption.candidateQueryData
+ )
+ ) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_SELECT_FROM_OPTION)
+ )
+ }
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(),
+ /*subType=*/null,
+ listOf(SLICE_HINT_LAST_USED_TIME_MILLIS)
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): PasswordCredentialEntry? {
+ var typeDisplayName: CharSequence? = null
+ var title: CharSequence? = null
+ var subTitle: CharSequence? = null
+ var icon: Icon? = null
+ var pendingIntent: PendingIntent? = null
+ var lastUsedTime: Instant? = null
+ var autoSelectAllowed = false
+ var autoSelectAllowedFromOption = false
+ var beginGetPasswordOptionId: CharSequence? = null
+ var isDefaultIcon = false
+
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+ typeDisplayName = it.text
+ } else if (it.hasHint(SLICE_HINT_TITLE)) {
+ title = it.text
+ } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
+ subTitle = it.text
+ } else if (it.hasHint(SLICE_HINT_ICON)) {
+ icon = it.icon
+ } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+ beginGetPasswordOptionId = it.text
+ } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTime = Instant.ofEpochMilli(it.long)
+ } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
+ val autoSelectValue = it.text
+ if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
+ autoSelectAllowed = true
+ }
+ } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
+ autoSelectAllowedFromOption = true
+ } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
+ isDefaultIcon = true
+ }
+ }
+
+ return try {
+ PasswordCredentialEntry(
+ title!!,
+ subTitle,
+ typeDisplayName!!,
+ pendingIntent!!,
+ lastUsedTime,
+ icon!!,
+ autoSelectAllowed,
+ BeginGetPasswordOption.createFromEntrySlice(
+ Bundle(),
+ beginGetPasswordOptionId!!.toString()
+ ),
+ autoSelectAllowedFromOption,
+ isDefaultIcon
+ )
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+
+ /** Builder for [PasswordCredentialEntry] */
+ class Builder(
+ private val context: Context,
+ private val username: CharSequence,
+ private val pendingIntent: PendingIntent,
+ private val beginGetPasswordOption: BeginGetPasswordOption
+ ) {
+ private var displayName: CharSequence? = null
+ private var lastUsedTime: Instant? = null
+ private var icon: Icon? = null
+ private var autoSelectAllowed = false
+
+ /** Sets a displayName to be shown on the UI with this entry */
+ fun setDisplayName(displayName: CharSequence?): Builder {
+ this.displayName = displayName
+ return this
+ }
+
+ /** Sets the icon to be shown on the UI with this entry */
+ fun setIcon(icon: Icon): Builder {
+ this.icon = icon
+ return this
+ }
+
+ /**
+ * Sets whether the entry should be auto-selected.
+ * The value is false by default
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
+ this.autoSelectAllowed = autoSelectAllowed
+ return this
+ }
+
+ /**
+ * Sets the last used time of this account. This information will be used to sort the
+ * entries on the selector.
+ */
+ fun setLastUsedTime(lastUsedTime: Instant?): Builder {
+ this.lastUsedTime = lastUsedTime
+ return this
+ }
+
+ /** Builds an instance of [PasswordCredentialEntry] */
+ fun build(): PasswordCredentialEntry {
+ if (icon == null) {
+ icon = Icon.createWithResource(context, R.drawable.ic_password)
+ }
+ val typeDisplayName = context.getString(
+ R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL
+ )
+ return PasswordCredentialEntry(
+ username,
+ displayName,
+ typeDisplayName,
+ pendingIntent,
+ lastUsedTime,
+ icon!!,
+ autoSelectAllowed,
+ beginGetPasswordOption
+ )
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt
new file mode 100644
index 0000000..9875032
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.Intent
+import android.service.credentials.BeginCreateCredentialResponse
+import android.service.credentials.CreateCredentialRequest
+import android.service.credentials.CredentialEntry
+import android.service.credentials.CredentialProviderService
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.credentials.CreateCredentialResponse
+import androidx.credentials.GetCredentialResponse
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.provider.utils.BeginGetCredentialUtil
+
+/**
+ * PendingIntentHandler to be used by credential providers to extract requests from
+ * [PendingIntent] invoked when a given [CreateEntry], or a [CustomCredentialEntry]
+ * is selected by the user.
+ *
+ * This handler can also be used to set [android.credentials.CreateCredentialResponse] and
+ * [android.credentials.GetCredentialResponse] on the result of the activity
+ * invoked by the [PendingIntent]
+ */
+@RequiresApi(34)
+class PendingIntentHandler {
+ companion object {
+ private const val TAG = "PendingIntentHandler"
+
+ /**
+ * Extracts the [ProviderCreateCredentialRequest] from the provider's
+ * [PendingIntent] invoked by the Android system.
+ *
+ * @param intent the intent associated with the [Activity] invoked through the
+ * [PendingIntent]
+ *
+ * @throws NullPointerException If [intent] is null
+ */
+ @JvmStatic
+ fun retrieveProviderCreateCredentialRequest(intent: Intent):
+ ProviderCreateCredentialRequest? {
+ val frameworkReq: CreateCredentialRequest? =
+ intent.getParcelableExtra(
+ CredentialProviderService
+ .EXTRA_CREATE_CREDENTIAL_REQUEST, CreateCredentialRequest::class.java
+ )
+ if (frameworkReq == null) {
+ Log.i(TAG, "Request not found in pendingIntent")
+ return frameworkReq
+ }
+ return ProviderCreateCredentialRequest(
+ androidx.credentials.CreateCredentialRequest
+ .createFrom(
+ frameworkReq.type,
+ frameworkReq.data,
+ frameworkReq.data,
+ requireSystemProvider = false,
+ frameworkReq.callingAppInfo.origin
+ ) ?: return null,
+ frameworkReq.callingAppInfo
+ )
+ }
+
+ /**
+ * Extracts the [BeginGetCredentialRequest] from the provider's
+ * [PendingIntent] invoked by the Android system when the user
+ * selects an [AuthenticationAction].
+ *
+ * @param intent the intent associated with the [Activity] invoked through the
+ * [PendingIntent]
+ *
+ * @throws NullPointerException If [intent] is null
+ */
+ @JvmStatic
+ fun retrieveBeginGetCredentialRequest(intent: Intent): BeginGetCredentialRequest? {
+ val request = intent.getParcelableExtra(
+ "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST",
+ android.service.credentials.BeginGetCredentialRequest::class.java
+ )
+ return request?.let { BeginGetCredentialUtil.convertToJetpackRequest(it) }
+ }
+
+ /**
+ * Sets the [CreateCredentialResponse] on the result of the
+ * activity invoked by the [PendingIntent] set on a
+ * [CreateEntry].
+ *
+ * @param intent the intent to be set on the result of the [Activity] invoked through the
+ * [PendingIntent]
+ * @param response the response to be set as an extra on the [intent]
+ *
+ * @throws NullPointerException If [intent], or [response] is null
+ */
+ @JvmStatic
+ fun setCreateCredentialResponse(
+ intent: Intent,
+ response: CreateCredentialResponse
+ ) {
+ intent.putExtra(
+ CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESPONSE,
+ android.credentials.CreateCredentialResponse(response.data)
+ )
+ }
+
+ /**
+ * Extracts the [ProviderGetCredentialRequest] from the provider's
+ * [PendingIntent] invoked by the Android system, when the user selects a
+ * [CredentialEntry].
+ *
+ * @param intent the intent associated with the [Activity] invoked through the
+ * [PendingIntent]
+ *
+ * @throws NullPointerException If [intent] is null
+ */
+ @JvmStatic
+ fun retrieveProviderGetCredentialRequest(intent: Intent):
+ ProviderGetCredentialRequest? {
+ val frameworkReq = intent.getParcelableExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+ android.service.credentials.GetCredentialRequest::class.java
+ )
+ if (frameworkReq == null) {
+ Log.i(TAG, "Get request from framework is null")
+ return null
+ }
+ return ProviderGetCredentialRequest.createFrom(frameworkReq)
+ }
+
+ /**
+ * Sets the [android.credentials.GetCredentialResponse] on the result of the
+ * activity invoked by the [PendingIntent], set on a [CreateEntry].
+ *
+ * @param intent the intent to be set on the result of the [Activity] invoked through the
+ * [PendingIntent]
+ * @param response the response to be set as an extra on the [intent]
+ *
+ * @throws NullPointerException If [intent], or [response] is null
+ */
+ @JvmStatic
+ fun setGetCredentialResponse(
+ intent: Intent,
+ response: GetCredentialResponse
+ ) {
+ intent.putExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+ android.credentials.GetCredentialResponse(
+ android.credentials.Credential(
+ response.credential.type,
+ response.credential.data
+ )
+ )
+ )
+ }
+
+ /**
+ * Sets the [android.service.credentials.BeginGetCredentialResponse] on the result of the
+ * activity invoked by the [PendingIntent], set on an [AuthenticationAction].
+ *
+ * @param intent the intent to be set on the result of the [Activity] invoked through the
+ * [PendingIntent]
+ * @param response the response to be set as an extra on the [intent]
+ *
+ * @throws NullPointerException If [intent], or [response] is null
+ */
+ @JvmStatic
+ fun setBeginGetCredentialResponse(
+ intent: Intent,
+ response: BeginGetCredentialResponse
+ ) {
+ intent.putExtra(
+ CredentialProviderService.EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE,
+ BeginGetCredentialUtil.convertToFrameworkResponse(response)
+ )
+ }
+
+ /**
+ * Sets the [androidx.credentials.exceptions.GetCredentialException] if an error is
+ * encountered during the final phase of the get credential flow.
+ *
+ * A credential provider service returns a list of [CredentialEntry] as part of
+ * the [BeginGetCredentialResponse] to the query phase of the get-credential flow.
+ * If the user selects one of these entries, the corresponding [PendingIntent]
+ * is fired and the provider's activity is invoked.
+ * If there is an error encountered during the lifetime of that activity, the provider
+ * must use this API to set an exception before finishing this activity.
+ *
+ * @param intent the intent to be set on the result of the [Activity] invoked through the
+ * [PendingIntent]
+ * @param exception the exception to be set as an extra to the [intent]
+ *
+ * @throws NullPointerException If [intent], or [exception] is null
+ */
+ @JvmStatic
+ fun setGetCredentialException(
+ intent: Intent,
+ exception: GetCredentialException
+ ) {
+ intent.putExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ android.credentials.GetCredentialException(exception.type, exception.message)
+ )
+ }
+
+ /**
+ * Sets the [androidx.credentials.exceptions.CreateCredentialException] if an error is
+ * encountered during the final phase of the create credential flow.
+ *
+ * A credential provider service returns a list of [CreateEntry] as part of
+ * the [BeginCreateCredentialResponse] to the query phase of the get-credential flow.
+ *
+ * If the user selects one of these entries, the corresponding [PendingIntent]
+ * is fired and the provider's activity is invoked. If there is an error encountered
+ * during the lifetime of that activity, the provider must use this API to set
+ * an exception before finishing the activity.
+ *
+ * @param intent the intent to be set on the result of the [Activity] invoked through the
+ * [PendingIntent]
+ * @param exception the exception to be set as an extra to the [intent]
+ *
+ * @throws NullPointerException If [intent], or [exception] is null
+ */
+ @JvmStatic
+ fun setCreateCredentialException(
+ intent: Intent,
+ exception: CreateCredentialException
+ ) {
+ intent.putExtra(
+ CredentialProviderService.EXTRA_CREATE_CREDENTIAL_EXCEPTION,
+ android.credentials.CreateCredentialException(exception.type, exception.message)
+ )
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderClearCredentialStateRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderClearCredentialStateRequest.kt
new file mode 100644
index 0000000..ff8d7eb
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderClearCredentialStateRequest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.service.credentials.CallingAppInfo
+
+/**
+ * Request class for clearing a user's credential state from the credential providers.
+ *
+ * @property callingAppInfo info pertaining to the calling app that's making the request
+ */
+class ProviderClearCredentialStateRequest constructor(val callingAppInfo: CallingAppInfo)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt
new file mode 100644
index 0000000..b5557ef
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.service.credentials.CallingAppInfo
+import androidx.credentials.CreateCredentialRequest
+
+/**
+ * Final request received by the provider after the user has selected a given [CreateEntry]
+ * on the UI.
+ *
+ * This request contains the actual request coming from the calling app,
+ * and the application information associated with the calling app.
+ *
+ * @property callingRequest the complete [CreateCredentialRequest] coming from
+ * the calling app that is requesting for credential creation
+ * @property callingAppInfo information pertaining to the calling app making
+ * the request
+ *
+ * @throws NullPointerException If [callingRequest] is null
+ * @throws NullPointerException If [callingAppInfo] is null
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ */
+class ProviderCreateCredentialRequest constructor(
+ val callingRequest: CreateCredentialRequest,
+ val callingAppInfo: CallingAppInfo
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt
new file mode 100644
index 0000000..e91c671
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.app.PendingIntent
+import android.service.credentials.CallingAppInfo
+import androidx.annotation.RequiresApi
+import androidx.credentials.CredentialOption
+import java.util.stream.Collectors
+
+/**
+ * Request received by the provider after the query phase of the get flow is complete i.e. the user
+ * was presented with a list of credentials, and the user has now made a selection from the list of
+ * [CredentialEntry] presented on the selector UI.
+ *
+ * This request will be added to the intent extras of the activity invoked by the [PendingIntent]
+ * set on the [CredentialEntry] that the user selected. The request
+ * must be extracted using the [PendingIntentHandler.retrieveProviderGetCredentialRequest] helper
+ * API.
+ *
+ * @property credentialOptions the list of credential retrieval options containing the
+ * required parameters.
+ * This list is expected to contain a single [CredentialOption] when this
+ * request is retrieved from the [android.app.Activity] invoked by the [android.app.PendingIntent]
+ * set on a [PasswordCredentialEntry] or a [PublicKeyCredentialEntry]. This is because these
+ * entries are created for a given [BeginGetPasswordOption] or a [BeginGetPublicKeyCredentialOption]
+ * respectively, which corresponds to a single [CredentialOption].
+ *
+ * This list is expected to contain multiple [CredentialOption] when this request is retrieved
+ * from the [android.app.Activity] invoked by the [android.app.PendingIntent]
+ * set on a [RemoteEntry]. This is because when a remote entry is selected. the entire
+ * request, containing multiple options, is sent to a remote device.
+ *
+ * @property callingAppInfo information pertaining to the calling application
+ *
+ * Note : Credential providers are not expected to utilize the constructor in this class for any
+ * production flow. This constructor must only be used for testing purposes.
+ */
+@RequiresApi(34)
+class ProviderGetCredentialRequest constructor(
+ val credentialOptions: List<CredentialOption>,
+ val callingAppInfo: CallingAppInfo
+) {
+
+ /** @hide */
+ companion object {
+ internal fun createFrom(request: android.service.credentials.GetCredentialRequest):
+ ProviderGetCredentialRequest {
+ return ProviderGetCredentialRequest(
+ request.credentialOptions.stream()
+ .map { option ->
+ CredentialOption.createFrom(
+ option.type,
+ option.credentialRetrievalData,
+ option.candidateQueryData,
+ option.isSystemProviderRequired,
+ option.allowedProviders,
+ )
+ }
+ .collect(Collectors.toList()),
+ request.callingAppInfo)
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
new file mode 100644
index 0000000..a4425c2
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.credentials.CredentialOption
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.R
+import java.time.Instant
+import java.util.Collections
+
+/**
+ * A public key credential entry that is displayed on the account selector UI. This
+ * entry denotes that a credential of type [PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL]
+ * is available for the user to select.
+ *
+ * Once this entry is selected, the corresponding [pendingIntent] will be invoked. The provider
+ * can then show any activity they wish to. Before finishing the activity, provider must
+ * set the final [androidx.credentials.GetCredentialResponse] through the
+ * [PendingIntentHandler.setGetCredentialResponse] helper API.
+ *
+ * @property username the username of the account holding the public key credential
+ * @property displayName the displayName of the account holding the public key credential
+ * @property lastUsedTime the last used time of this entry
+ * @property icon the icon to be displayed with this entry on the selector. If not set, a
+ * default icon representing a public key credential type is set by the library
+ * @param pendingIntent the [PendingIntent] to be invoked when the user
+ * selects this entry
+ * @property isAutoSelectAllowed whether this entry is allowed to be auto
+ * selected if it is the only one on the UI. Note that setting this value
+ * to true does not guarantee this behavior. The developer must also set this
+ * to true, and the framework must determine that it is safe to auto select.
+ *
+ * @throws IllegalArgumentException if [username] is empty
+ */
+@RequiresApi(28)
+class PublicKeyCredentialEntry internal constructor(
+ val username: CharSequence,
+ val displayName: CharSequence?,
+ val typeDisplayName: CharSequence,
+ val pendingIntent: PendingIntent,
+ val icon: Icon,
+ val lastUsedTime: Instant?,
+ val isAutoSelectAllowed: Boolean,
+ beginGetPublicKeyCredentialOption: BeginGetPublicKeyCredentialOption,
+ /** @hide */
+ val autoSelectAllowedFromOption: Boolean = false,
+ /** @hide */
+ val isDefaultIcon: Boolean = false
+) : CredentialEntry(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ beginGetPublicKeyCredentialOption,
+ toSlice(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ username,
+ displayName,
+ pendingIntent,
+ typeDisplayName,
+ lastUsedTime,
+ icon,
+ isAutoSelectAllowed,
+ beginGetPublicKeyCredentialOption
+ )
+) {
+
+ init {
+ require(username.isNotEmpty()) { "username must not be empty" }
+ require(typeDisplayName.isNotEmpty()) { "typeDisplayName must not be empty" }
+ }
+
+ constructor(
+ context: Context,
+ username: CharSequence,
+ pendingIntent: PendingIntent,
+ beginGetPublicKeyCredentialOption: BeginGetPublicKeyCredentialOption,
+ displayName: CharSequence? = null,
+ lastUsedTime: Instant? = null,
+ icon: Icon = Icon.createWithResource(context, R.drawable.ic_passkey),
+ isAutoSelectAllowed: Boolean = false,
+ ) : this(
+ username,
+ displayName,
+ context.getString(
+ R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL
+ ),
+ pendingIntent,
+ icon,
+ lastUsedTime,
+ isAutoSelectAllowed,
+ beginGetPublicKeyCredentialOption
+ )
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "PublicKeyCredEntry"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_TITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_SUBTITLE =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_ICON =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_ALLOWED =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_OPTION_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_DEFAULT_ICON_RES_ID =
+ "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_TRUE_STRING = "true"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val AUTO_SELECT_FALSE_STRING = "false"
+
+ /** @hide */
+ @RequiresApi(28)
+ @JvmStatic
+ fun toSlice(
+ type: String,
+ title: CharSequence,
+ subTitle: CharSequence?,
+ pendingIntent: PendingIntent,
+ typeDisplayName: CharSequence?,
+ lastUsedTime: Instant?,
+ icon: Icon,
+ isAutoSelectAllowed: Boolean,
+ beginGetPublicKeyCredentialOption: BeginGetPublicKeyCredentialOption
+ ): Slice {
+ // TODO("Put the right revision value")
+ val autoSelectAllowed = if (isAutoSelectAllowed) {
+ AUTO_SELECT_TRUE_STRING
+ } else {
+ AUTO_SELECT_FALSE_STRING
+ }
+ val sliceBuilder = Slice.Builder(
+ Uri.EMPTY, SliceSpec(
+ type, 1
+ )
+ )
+ .addText(
+ typeDisplayName, /*subType=*/null,
+ listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
+ )
+ .addText(
+ title, /*subType=*/null,
+ listOf(SLICE_HINT_TITLE)
+ )
+ .addText(
+ subTitle, /*subType=*/null,
+ listOf(SLICE_HINT_SUBTITLE)
+ )
+ .addText(
+ autoSelectAllowed, /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_ALLOWED)
+ )
+ .addText(
+ beginGetPublicKeyCredentialOption.id,
+ /*subType=*/null,
+ listOf(SLICE_HINT_OPTION_ID)
+ )
+ .addIcon(
+ icon, /*subType=*/null,
+ listOf(SLICE_HINT_ICON)
+ )
+ try {
+ if (icon.resId == R.drawable.ic_passkey) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_DEFAULT_ICON_RES_ID)
+ )
+ }
+ } catch (_: IllegalStateException) {
+ }
+
+ if (CredentialOption.extractAutoSelectValue(
+ beginGetPublicKeyCredentialOption.candidateQueryData
+ )
+ ) {
+ sliceBuilder.addInt(
+ /*true=*/1,
+ /*subType=*/null,
+ listOf(SLICE_HINT_AUTO_SELECT_FROM_OPTION)
+ )
+ }
+ if (lastUsedTime != null) {
+ sliceBuilder.addLong(
+ lastUsedTime.toEpochMilli(),
+ /*subType=*/null,
+ listOf(SLICE_HINT_LAST_USED_TIME_MILLIS)
+ )
+ }
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(),
+ /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @RequiresApi(28)
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {
+ var typeDisplayName: CharSequence? = null
+ var title: CharSequence? = null
+ var subTitle: CharSequence? = null
+ var icon: Icon? = null
+ var pendingIntent: PendingIntent? = null
+ var lastUsedTime: Instant? = null
+ var autoSelectAllowed = false
+ var beginGetPublicKeyCredentialOptionId: CharSequence? = null
+ var autoSelectAllowedFromOption = false
+ var isDefaultIcon = false
+
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+ typeDisplayName = it.text
+ } else if (it.hasHint(SLICE_HINT_TITLE)) {
+ title = it.text
+ } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
+ subTitle = it.text
+ } else if (it.hasHint(SLICE_HINT_ICON)) {
+ icon = it.icon
+ } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+ beginGetPublicKeyCredentialOptionId = it.text
+ } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+ lastUsedTime = Instant.ofEpochMilli(it.long)
+ } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
+ val autoSelectValue = it.text
+ if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
+ autoSelectAllowed = true
+ }
+ } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
+ autoSelectAllowedFromOption = true
+ } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
+ isDefaultIcon = true
+ }
+ }
+
+ return try {
+ PublicKeyCredentialEntry(
+ title!!,
+ subTitle,
+ typeDisplayName!!,
+ pendingIntent!!,
+ icon!!,
+ lastUsedTime,
+ autoSelectAllowed,
+ BeginGetPublicKeyCredentialOption.createFromEntrySlice(
+ Bundle(),
+ beginGetPublicKeyCredentialOptionId!!.toString()
+ ),
+ autoSelectAllowedFromOption,
+ isDefaultIcon
+ )
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+
+ /**
+ * Builder for [PublicKeyCredentialEntry]
+ */
+ class Builder(
+ private val context: Context,
+ private val username: CharSequence,
+ private val pendingIntent: PendingIntent,
+ private val beginGetPublicKeyCredentialOption: BeginGetPublicKeyCredentialOption
+ ) {
+ private var displayName: CharSequence? = null
+ private var lastUsedTime: Instant? = null
+ private var icon: Icon? = null
+ private var autoSelectAllowed: Boolean = false
+
+ /** Sets a displayName to be shown on the UI with this entry */
+ fun setDisplayName(displayName: CharSequence?): Builder {
+ this.displayName = displayName
+ return this
+ }
+
+ /** Sets the icon to be shown on the UI with this entry */
+ fun setIcon(icon: Icon): Builder {
+ this.icon = icon
+ return this
+ }
+
+ /**
+ * Sets whether the entry should be auto-selected.
+ * The value is false by default
+ */
+ @Suppress("MissingGetterMatchingBuilder")
+ fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
+ this.autoSelectAllowed = autoSelectAllowed
+ return this
+ }
+
+ /**
+ * Sets the last used time of this account
+ *
+ * This information will be used to sort the entries on the selector.
+ */
+ fun setLastUsedTime(lastUsedTime: Instant?): Builder {
+ this.lastUsedTime = lastUsedTime
+ return this
+ }
+
+ /** Builds an instance of [PublicKeyCredentialEntry] */
+ fun build(): PublicKeyCredentialEntry {
+ if (icon == null) {
+ icon = Icon.createWithResource(context, R.drawable.ic_passkey)
+ }
+ val typeDisplayName = context.getString(
+ R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL
+ )
+ return PublicKeyCredentialEntry(
+ username,
+ displayName,
+ typeDisplayName,
+ pendingIntent,
+ icon!!,
+ lastUsedTime,
+ autoSelectAllowed,
+ beginGetPublicKeyCredentialOption
+ )
+ }
+ }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
new file mode 100644
index 0000000..ba2831a
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.credentials.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.net.Uri
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import java.util.Collections
+
+/**
+ * An entry on the selector, denoting that the credential request will be completed on a remote
+ * device.
+ *
+ * Once this entry is selected, the corresponding [pendingIntent] will be invoked. The provider
+ * can then show any activity they wish to. Before finishing the activity, provider must
+ * set the final [androidx.credentials.GetCredentialResponse] through the
+ * [PendingIntentHandler.setGetCredentialResponse] helper API, or a
+ * [androidx.credentials.CreateCredentialResponse] through the
+ * [PendingIntentHandler.setCreateCredentialResponse] helper API depending on whether it is a get
+ * or create flow.
+ *
+ * @property pendingIntent the [PendingIntent] to be invoked when the user selects
+ * this entry
+ *
+ * See [android.service.credentials.BeginGetCredentialResponse] for usage details.
+ */
+class RemoteEntry constructor(
+ val pendingIntent: PendingIntent
+) {
+
+ /** @hide **/
+ @Suppress("AcronymName")
+ companion object {
+ private const val TAG = "RemoteEntry"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal const val SLICE_HINT_PENDING_INTENT =
+ "androidx.credentials.provider.remoteEntry.SLICE_HINT_PENDING_INTENT"
+
+ /** @hide */
+ @RequiresApi(28)
+ @JvmStatic
+ fun toSlice(
+ remoteEntry: RemoteEntry
+ ): Slice {
+ val pendingIntent = remoteEntry.pendingIntent
+ // TODO("Put the right spec and version value")
+ val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
+ sliceBuilder.addAction(
+ pendingIntent,
+ Slice.Builder(sliceBuilder)
+ .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+ .build(), /*subType=*/null
+ )
+ return sliceBuilder.build()
+ }
+
+ /**
+ * Returns an instance of [RemoteEntry] derived from a [Slice] object.
+ *
+ * @param slice the [Slice] object constructed through [toSlice]
+ *
+ * @hide
+ */
+ @RequiresApi(28)
+ @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+ @JvmStatic
+ fun fromSlice(slice: Slice): RemoteEntry? {
+ var pendingIntent: PendingIntent? = null
+ slice.items.forEach {
+ if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+ pendingIntent = it.action
+ }
+ }
+ return try {
+ RemoteEntry(pendingIntent!!)
+ } catch (e: Exception) {
+ Log.i(TAG, "fromSlice failed with: " + e.message)
+ null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginCreateCredentialUtil.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginCreateCredentialUtil.kt
new file mode 100644
index 0000000..0669c20
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginCreateCredentialUtil.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.utils
+
+import android.annotation.SuppressLint
+import androidx.annotation.RequiresApi
+import androidx.credentials.PasswordCredential
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.internal.FrameworkClassParsingException
+import androidx.credentials.provider.BeginCreateCredentialRequest
+import androidx.credentials.provider.BeginCreateCredentialResponse
+import androidx.credentials.provider.BeginCreateCustomCredentialRequest
+import androidx.credentials.provider.BeginCreatePasswordCredentialRequest
+import androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest
+import androidx.credentials.provider.CreateEntry
+import androidx.credentials.provider.RemoteEntry
+import java.util.stream.Collectors
+
+/**
+ * @hide
+ */
+@RequiresApi(34)
+class BeginCreateCredentialUtil {
+ companion object {
+ @JvmStatic
+ internal fun convertToJetpackRequest(
+ request: android.service.credentials.BeginCreateCredentialRequest
+ ):
+ BeginCreateCredentialRequest {
+ return try {
+ when (request.type) {
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL -> {
+ BeginCreatePasswordCredentialRequest.createFrom(
+ request.data, request.callingAppInfo
+ )
+ }
+
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> {
+ BeginCreatePublicKeyCredentialRequest.createFrom(
+ request.data, request.callingAppInfo
+ )
+ }
+
+ else -> {
+ BeginCreateCustomCredentialRequest(
+ request.type, request.data,
+ request.callingAppInfo
+ )
+ }
+ }
+ } catch (e: FrameworkClassParsingException) {
+ BeginCreateCustomCredentialRequest(
+ request.type,
+ request.data,
+ request.callingAppInfo
+ )
+ }
+ }
+
+ fun convertToFrameworkResponse(
+ response: BeginCreateCredentialResponse
+ ): android.service.credentials.BeginCreateCredentialResponse {
+ val frameworkBuilder = android.service.credentials.BeginCreateCredentialResponse
+ .Builder()
+ populateCreateEntries(frameworkBuilder, response.createEntries)
+ populateRemoteEntry(frameworkBuilder, response.remoteEntry)
+ return frameworkBuilder.build()
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun populateRemoteEntry(
+ frameworkBuilder: android.service.credentials.BeginCreateCredentialResponse.Builder,
+ remoteEntry: RemoteEntry?
+ ) {
+ if (remoteEntry == null) {
+ return
+ }
+ frameworkBuilder.setRemoteCreateEntry(
+ android.service.credentials.RemoteEntry(
+ RemoteEntry.toSlice(remoteEntry)
+ )
+ )
+ }
+
+ private fun populateCreateEntries(
+ frameworkBuilder: android.service.credentials.BeginCreateCredentialResponse.Builder,
+ createEntries: List<CreateEntry>
+ ) {
+ createEntries.forEach {
+ frameworkBuilder.addCreateEntry(
+ android.service.credentials.CreateEntry(
+ CreateEntry.toSlice(it)
+ )
+ )
+ }
+ }
+
+ fun convertToFrameworkRequest(request: BeginCreateCredentialRequest):
+ android.service.credentials.BeginCreateCredentialRequest {
+ return android.service.credentials.BeginCreateCredentialRequest(request.type,
+ request.candidateQueryData, request.callingAppInfo)
+ }
+
+ fun convertToJetpackResponse(
+ frameworkResponse: android.service.credentials.BeginCreateCredentialResponse
+ ): BeginCreateCredentialResponse {
+ return BeginCreateCredentialResponse(
+ createEntries = frameworkResponse.createEntries.stream()
+ .map { entry -> CreateEntry.fromSlice(entry.slice) }
+ .filter { entry -> entry != null }
+ .map { entry -> entry!! }
+ .collect(Collectors.toList()),
+ remoteEntry =
+ frameworkResponse.remoteCreateEntry?.let { RemoteEntry.fromSlice(it.slice) }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginGetCredentialUtil.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginGetCredentialUtil.kt
new file mode 100644
index 0000000..fb7735e
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/BeginGetCredentialUtil.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.utils
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.BeginGetCredentialOption
+import androidx.credentials.provider.BeginGetCredentialRequest
+import androidx.credentials.provider.BeginGetCredentialResponse
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import java.util.stream.Collectors
+
+@RequiresApi(34)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class BeginGetCredentialUtil {
+ companion object {
+ @JvmStatic
+ internal fun convertToJetpackRequest(
+ request: android.service.credentials.BeginGetCredentialRequest
+ ): BeginGetCredentialRequest {
+ val beginGetCredentialOptions: MutableList<BeginGetCredentialOption> =
+ mutableListOf()
+ request.beginGetCredentialOptions.forEach {
+ beginGetCredentialOptions.add(
+ BeginGetCredentialOption.createFrom(
+ it.id, it.type, it.candidateQueryData
+ )
+ )
+ }
+ return BeginGetCredentialRequest(
+ callingAppInfo = request.callingAppInfo,
+ beginGetCredentialOptions = beginGetCredentialOptions
+ )
+ }
+
+ fun convertToFrameworkResponse(response: BeginGetCredentialResponse):
+ android.service.credentials.BeginGetCredentialResponse {
+ val frameworkBuilder = android.service.credentials.BeginGetCredentialResponse.Builder()
+ populateCredentialEntries(frameworkBuilder, response.credentialEntries)
+ populateActionEntries(frameworkBuilder, response.actions)
+ populateAuthenticationEntries(frameworkBuilder, response.authenticationActions)
+ populateRemoteEntry(frameworkBuilder, response.remoteEntry)
+ return frameworkBuilder.build()
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun populateRemoteEntry(
+ frameworkBuilder: android.service.credentials.BeginGetCredentialResponse.Builder,
+ remoteEntry: RemoteEntry?
+ ) {
+ if (remoteEntry == null) {
+ return
+ }
+ frameworkBuilder.setRemoteCredentialEntry(
+ android.service.credentials.RemoteEntry(RemoteEntry.toSlice(remoteEntry))
+ )
+ }
+
+ private fun populateAuthenticationEntries(
+ frameworkBuilder: android.service.credentials.BeginGetCredentialResponse.Builder,
+ authenticationActions: List<AuthenticationAction>
+ ) {
+ authenticationActions.forEach {
+ frameworkBuilder.addAuthenticationAction(
+ android.service.credentials.Action(
+ AuthenticationAction.toSlice(it)
+ )
+ )
+ }
+ }
+
+ private fun populateActionEntries(
+ builder: android.service.credentials.BeginGetCredentialResponse.Builder,
+ actionEntries: List<Action>
+ ) {
+ actionEntries.forEach {
+ builder.addAction(
+ android.service.credentials.Action(
+ Action.toSlice(it)
+ )
+ )
+ }
+ }
+
+ private fun populateCredentialEntries(
+ builder: android.service.credentials.BeginGetCredentialResponse.Builder,
+ credentialEntries: List<CredentialEntry>
+ ) {
+ credentialEntries.forEach {
+ builder.addCredentialEntry(
+ android.service.credentials.CredentialEntry(
+ android.service.credentials.BeginGetCredentialOption(
+ it.beginGetCredentialOption.id,
+ it.type,
+ Bundle.EMPTY
+ ),
+ it.slice
+ )
+ )
+ }
+ }
+
+ fun convertToFrameworkRequest(request: BeginGetCredentialRequest):
+ android.service.credentials.BeginGetCredentialRequest {
+ return android.service.credentials.BeginGetCredentialRequest.Builder()
+ .setCallingAppInfo(request.callingAppInfo)
+ .setBeginGetCredentialOptions(request.beginGetCredentialOptions.stream()
+ .map { option -> convertToJetpackBeginOption(option) }
+ .collect(Collectors.toList()))
+ .build()
+ }
+
+ private fun convertToJetpackBeginOption(option: BeginGetCredentialOption):
+ android.service.credentials.BeginGetCredentialOption {
+ return android.service.credentials.BeginGetCredentialOption(option.id, option.type,
+ option.candidateQueryData)
+ }
+
+ fun convertToJetpackResponse(
+ response: android.service.credentials.BeginGetCredentialResponse
+ ): BeginGetCredentialResponse {
+ return BeginGetCredentialResponse(
+ credentialEntries = response.credentialEntries.stream()
+ .map { entry -> CredentialEntry.createFrom(entry.slice) }
+ .filter { entry -> entry != null }
+ .map { entry -> entry!! }
+ .collect(Collectors.toList()),
+ actions = response.actions.stream()
+ .map { entry -> Action.fromSlice(entry.slice) }
+ .filter { entry -> entry != null }
+ .map { entry -> entry!! }
+ .collect(Collectors.toList()),
+ authenticationActions = response.authenticationActions.stream()
+ .map { entry -> AuthenticationAction.fromSlice(entry.slice) }
+ .filter { entry -> entry != null }
+ .map { entry -> entry!! }
+ .collect(Collectors.toList()),
+ remoteEntry =
+ response.remoteCredentialEntry?.let { RemoteEntry.fromSlice(it.slice) }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/utils/ClearCredentialUtil.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/ClearCredentialUtil.kt
new file mode 100644
index 0000000..460a368
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/ClearCredentialUtil.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.utils
+
+import android.service.credentials.ClearCredentialStateRequest
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.credentials.provider.ProviderClearCredentialStateRequest
+
+@RequiresApi(34)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ClearCredentialUtil {
+ companion object {
+ @JvmStatic
+ internal fun convertToJetpackRequest(request: ClearCredentialStateRequest):
+ ProviderClearCredentialStateRequest {
+ return ProviderClearCredentialStateRequest(request.callingAppInfo)
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/res/values-ky/strings.xml b/credentials/credentials/src/main/res/values-ky/strings.xml
index 3366129..240c775 100644
--- a/credentials/credentials/src/main/res/values-ky/strings.xml
+++ b/credentials/credentials/src/main/res/values-ky/strings.xml
@@ -17,6 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Мүмкүндүк алуу ачкычы"</string>
+ <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Киргизүүчү ачкыч"</string>
<string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"Сырсөз"</string>
</resources>
diff --git a/credentials/credentials/src/main/res/values-ta/strings.xml b/credentials/credentials/src/main/res/values-ta/strings.xml
index 458bcb4..d3f9b1f 100644
--- a/credentials/credentials/src/main/res/values-ta/strings.xml
+++ b/credentials/credentials/src/main/res/values-ta/strings.xml
@@ -17,6 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"கடவுக்குறியீடு"</string>
+ <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"கடவுச்சாவி"</string>
<string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"கடவுச்சொல்"</string>
</resources>
diff --git a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/SimpleActorTest.kt b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/SimpleActorTest.kt
index e6446b5..154e868 100644
--- a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/SimpleActorTest.kt
+++ b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/SimpleActorTest.kt
@@ -73,6 +73,7 @@
assertThat(msgs).isEqualTo(listOf(1, 2, 3, 4))
}
+ @Ignore("b/281516026")
@Test
fun testOnCompleteIsCalledWhenScopeIsCancelled() = runBlocking<Unit> {
val scope = CoroutineScope(Job())
diff --git a/development/studio/idea.properties b/development/studio/idea.properties
index f352237..3cabbbf 100644
--- a/development/studio/idea.properties
+++ b/development/studio/idea.properties
@@ -5,12 +5,12 @@
#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to IDE config folder. Make sure you're using forward slashes.
#---------------------------------------------------------------------
-idea.config.path=${user.home}/.AndroidStudioAndroidX/config
+idea.config.path=${user.home}/.AndroidStudioAndroidXPlatform/config
#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to IDE system folder. Make sure you're using forward slashes.
#---------------------------------------------------------------------
-idea.system.path=${user.home}/.AndroidStudioAndroidX/system
+idea.system.path=${user.home}/.AndroidStudioAndroidXPlatform/system
#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to user installed plugins folder. Make sure you're using forward slashes.
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 527b2e0..c07ac43 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -123,6 +123,7 @@
docs("androidx.core:core:1.12.0-alpha04")
docs("androidx.core:core-ktx:1.12.0-alpha03")
docs("androidx.core:core-splashscreen:1.1.0-alpha01")
+ docs("androidx.core:core-telecom:1.0.0-alpha01")
docs("androidx.core:core-testing:1.12.0-alpha03")
docs("androidx.credentials:credentials:1.2.0-alpha03")
docs("androidx.credentials:credentials-play-services-auth:1.2.0-alpha03")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 265ae34..7b37b12 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -133,6 +133,7 @@
docs(project(":core:core-remoteviews"))
docs(project(":core:core-splashscreen"))
docs(project(":core:core-role"))
+ docs(project(":core:core-telecom"))
docs(project(":core:core-testing"))
docs(project(":core:uwb:uwb"))
docs(project(":core:uwb:uwb-rxjava3"))
@@ -178,6 +179,7 @@
docs(project(":glance:glance-wear-tiles"))
docs(project(":graphics:filters:filters"))
docs(project(":graphics:graphics-core"))
+ docs(project(":graphics:graphics-path"))
docs(project(":graphics:graphics-shapes"))
docs(project(":gridlayout:gridlayout"))
docs(project(":health:connect:connect-client"))
@@ -365,12 +367,12 @@
docs(project(":wear:watchface:watchface-style"))
docs(project(":webkit:webkit"))
docs(project(":window:window"))
+ samples(project(":window:window-samples"))
docs(project(":window:window-core"))
docs(project(":window:window-java"))
docs(project(":window:window-rxjava2"))
docs(project(":window:window-rxjava3"))
stubs(project(":window:sidecar:sidecar"))
- samples(project(":window:window-samples"))
stubs(project(":window:extensions:extensions"))
stubs(project(":window:extensions:core:core"))
docs(project(":window:window-testing"))
diff --git a/drawerlayout/drawerlayout/api/api_lint.ignore b/drawerlayout/drawerlayout/api/api_lint.ignore
index be4e831..69b398e 100644
--- a/drawerlayout/drawerlayout/api/api_lint.ignore
+++ b/drawerlayout/drawerlayout/api/api_lint.ignore
@@ -3,12 +3,6 @@
Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
-InvalidNullabilityOverride: androidx.drawerlayout.widget.DrawerLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
- Invalid nullability on parameter `canvas` in method `drawChild`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.drawerlayout.widget.DrawerLayout#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-
-
ListenerInterface: androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener:
Listeners should be an interface, or otherwise renamed Callback: SimpleDrawerListener
@@ -23,6 +17,8 @@
Missing nullability on parameter `p` in method `checkLayoutParams`
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#dispatchGenericMotionEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `event` in method `dispatchGenericMotionEvent`
+MissingNullability: androidx.drawerlayout.widget.DrawerLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
+ Missing nullability on parameter `canvas` in method `drawChild`
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #1:
Missing nullability on parameter `child` in method `drawChild`
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#generateDefaultLayoutParams():
@@ -35,6 +31,8 @@
Missing nullability on method `generateLayoutParams` return
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#generateLayoutParams(android.view.ViewGroup.LayoutParams) parameter #0:
Missing nullability on parameter `p` in method `generateLayoutParams`
+MissingNullability: androidx.drawerlayout.widget.DrawerLayout#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `onDraw`
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#onInterceptTouchEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `ev` in method `onInterceptTouchEvent`
MissingNullability: androidx.drawerlayout.widget.DrawerLayout#onKeyDown(int, android.view.KeyEvent) parameter #1:
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt
index b1e7b2f..e000eb32 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/PopupViewHelper.kt
@@ -159,9 +159,9 @@
private val radius = resources.getDimension(R.dimen.emoji_picker_skin_tone_circle_radius)
var paint: Paint? = null
- override fun draw(canvas: Canvas?) {
+ override fun draw(canvas: Canvas) {
super.draw(canvas)
- canvas?.apply {
+ canvas.apply {
paint?.let { drawCircle(width / 2f, height / 2f, radius, it) }
}
}
diff --git a/glance/glance-wear-tiles/src/test/resources/robolectric.properties b/glance/glance-wear-tiles/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/glance/glance-wear-tiles/src/test/resources/robolectric.properties
+++ b/glance/glance-wear-tiles/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/glance/glance/src/test/resources/robolectric.properties b/glance/glance/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/glance/glance/src/test/resources/robolectric.properties
+++ b/glance/glance/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/gradle.properties b/gradle.properties
index 58fa1e5..d5433b4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -26,14 +26,14 @@
android.forceJacocoOutOfProcess=true
android.experimental.lint.missingBaselineIsEmptyBaseline=true
-# Generate versioned API files
-androidx.writeVersionedApiFiles=true
+# Don't generate versioned API files
+androidx.writeVersionedApiFiles=false
-# Run the CheckAarMetadata task
-android.experimental.disableCompileSdkChecks=false
+# Don't run the CheckAarMetadata task
+android.experimental.disableCompileSdkChecks=true
-# Do restrict compileSdkPreview usage
-androidx.allowCustomCompileSdk=false
+# Don't restrict compileSdkPreview usage
+androidx.allowCustomCompileSdk=true
# Comma-delimited lists of project path prefixes which have been opted-out of or opted-in to the
# Suppress Compatibility migration. Opt-out is matched first.
@@ -54,7 +54,7 @@
androidx.suppress.compatibility.optin.uvwxyz=u,v,w,x,y,z
# Don't warn about needing to update AGP
-android.suppressUnsupportedCompileSdk=UpsideDownCake,VanillaIceCream,33
+android.suppressUnsupportedCompileSdk=UpsideDownCake,VanillaIceCream,33,34
# Disable features we do not use
android.defaults.buildfeatures.aidl=false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 07851ae..c0e5a6f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -222,6 +222,7 @@
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.3.7" }
okhttpMockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version = "3.14.7" }
okio = { module = "com.squareup.okio:okio", version = "3.1.0" }
+opentest4j = { module = "org.opentest4j:opentest4j", version = "1.2.0" }
playFeatureDelivery = { module = "com.google.android.play:feature-delivery", version = "2.0.1" }
playCore = { module = "com.google.android.play:core", version = "1.10.3" }
playServicesAuth = {module = "com.google.android.gms:play-services-auth", version = "20.5.0"}
diff --git a/graphics/filters/filters/lint-baseline.xml b/graphics/filters/filters/lint-baseline.xml
index efe83da..9a42e316 100644
--- a/graphics/filters/filters/lint-baseline.xml
+++ b/graphics/filters/filters/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.1.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.1.0-alpha07">
<issue
id="UnsafeOptInUsageError"
diff --git a/graphics/graphics-core/api/current.txt b/graphics/graphics-core/api/current.txt
index d9f2b2f..a0a1a70 100644
--- a/graphics/graphics-core/api/current.txt
+++ b/graphics/graphics-core/api/current.txt
@@ -297,6 +297,8 @@
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setBufferTransform(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int transformation);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setCrop(androidx.graphics.surface.SurfaceControlCompat surfaceControl, android.graphics.Rect? crop);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setDamageRegion(androidx.graphics.surface.SurfaceControlCompat surfaceControl, android.graphics.Region? region);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.graphics.surface.SurfaceControlCompat.Transaction setDataSpace(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int dataSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public androidx.graphics.surface.SurfaceControlCompat.Transaction setExtendedRangeBrightness(androidx.graphics.surface.SurfaceControlCompat surfaceControl, @FloatRange(from=1.0, fromInclusive=true) float currentBufferRatio, @FloatRange(from=1.0, fromInclusive=true) float desiredRatio);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setLayer(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int z);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setOpaque(androidx.graphics.surface.SurfaceControlCompat surfaceControl, boolean isOpaque);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setPosition(androidx.graphics.surface.SurfaceControlCompat surfaceControl, float x, float y);
diff --git a/graphics/graphics-core/api/restricted_current.txt b/graphics/graphics-core/api/restricted_current.txt
index cf8f638..d0e90db 100644
--- a/graphics/graphics-core/api/restricted_current.txt
+++ b/graphics/graphics-core/api/restricted_current.txt
@@ -298,6 +298,8 @@
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setBufferTransform(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int transformation);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setCrop(androidx.graphics.surface.SurfaceControlCompat surfaceControl, android.graphics.Rect? crop);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setDamageRegion(androidx.graphics.surface.SurfaceControlCompat surfaceControl, android.graphics.Region? region);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.graphics.surface.SurfaceControlCompat.Transaction setDataSpace(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int dataSpace);
+ method @RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public androidx.graphics.surface.SurfaceControlCompat.Transaction setExtendedRangeBrightness(androidx.graphics.surface.SurfaceControlCompat surfaceControl, @FloatRange(from=1.0, fromInclusive=true) float currentBufferRatio, @FloatRange(from=1.0, fromInclusive=true) float desiredRatio);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setLayer(androidx.graphics.surface.SurfaceControlCompat surfaceControl, int z);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setOpaque(androidx.graphics.surface.SurfaceControlCompat surfaceControl, boolean isOpaque);
method public androidx.graphics.surface.SurfaceControlCompat.Transaction setPosition(androidx.graphics.surface.SurfaceControlCompat surfaceControl, float x, float y);
diff --git a/graphics/graphics-core/build.gradle b/graphics/graphics-core/build.gradle
index b99b3c8..22bc7e9 100644
--- a/graphics/graphics-core/build.gradle
+++ b/graphics/graphics-core/build.gradle
@@ -26,6 +26,7 @@
dependencies {
api(libs.kotlinStdlib)
implementation 'androidx.annotation:annotation:1.2.0'
+ implementation("androidx.core:core:1.8.0")
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
diff --git a/graphics/graphics-core/lint-baseline.xml b/graphics/graphics-core/lint-baseline.xml
new file mode 100644
index 0000000..a01fd91
--- /dev/null
+++ b/graphics/graphics-core/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" return if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRenderer.kt"/>
+ </issue>
+
+</issues>
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34Test.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34Test.kt
new file mode 100644
index 0000000..b1ade98
--- /dev/null
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34Test.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.graphics.lowlatency
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.hardware.HardwareBuffer
+import android.os.Build
+import androidx.core.os.BuildCompat
+import androidx.graphics.drawSquares
+import androidx.graphics.isAllColor
+import androidx.graphics.opengl.egl.supportsNativeAndroidFence
+import androidx.graphics.surface.SurfaceControlCompat
+import androidx.graphics.surface.SurfaceControlCompat.Companion.BUFFER_TRANSFORM_IDENTITY
+import androidx.graphics.verifyQuadrants
+import androidx.graphics.withEgl
+import androidx.hardware.SyncFenceCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class SingleBufferedCanvasRendererV34Test {
+
+ companion object {
+ const val TEST_WIDTH = 20
+ const val TEST_HEIGHT = 20
+ }
+
+ data class RectColors(
+ val topLeft: Int,
+ val topRight: Int,
+ val bottomLeft: Int,
+ val bottomRight: Int
+ )
+
+ @Test
+ fun testRenderFrameRotate0() {
+ testRenderWithTransform(
+ BUFFER_TRANSFORM_IDENTITY,
+ RectColors(
+ topLeft = Color.RED,
+ topRight = Color.YELLOW,
+ bottomRight = Color.BLUE,
+ bottomLeft = Color.GREEN
+ ),
+ RectColors(
+ topLeft = Color.RED,
+ topRight = Color.YELLOW,
+ bottomRight = Color.BLUE,
+ bottomLeft = Color.GREEN
+ )
+ )
+ }
+
+ @Test
+ fun testRenderFrameRotate90() {
+ testRenderWithTransform(
+ SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90,
+ RectColors(
+ topLeft = Color.RED,
+ topRight = Color.YELLOW,
+ bottomRight = Color.BLUE,
+ bottomLeft = Color.GREEN
+ ),
+ RectColors(
+ topLeft = Color.YELLOW,
+ topRight = Color.BLUE,
+ bottomRight = Color.GREEN,
+ bottomLeft = Color.RED
+ )
+ )
+ }
+
+ @Test
+ fun testRenderFrameRotate180() {
+ testRenderWithTransform(
+ SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180,
+ RectColors(
+ topLeft = Color.RED,
+ topRight = Color.YELLOW,
+ bottomRight = Color.BLUE,
+ bottomLeft = Color.GREEN
+ ),
+ RectColors(
+ topLeft = Color.BLUE,
+ topRight = Color.GREEN,
+ bottomRight = Color.RED,
+ bottomLeft = Color.YELLOW
+ )
+ )
+ }
+
+ @Test
+ fun testRenderFrameRotate270() {
+ testRenderWithTransform(
+ SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270,
+ RectColors(
+ topLeft = Color.RED,
+ topRight = Color.YELLOW,
+ bottomRight = Color.BLUE,
+ bottomLeft = Color.GREEN
+ ),
+ RectColors(
+ topLeft = Color.GREEN,
+ topRight = Color.RED,
+ bottomRight = Color.YELLOW,
+ bottomLeft = Color.BLUE
+ )
+ )
+ }
+
+ @Test
+ fun testClearRenderer() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val transformer = BufferTransformer().apply {
+ computeTransform(TEST_WIDTH, TEST_HEIGHT, BUFFER_TRANSFORM_IDENTITY)
+ }
+ val executor = Executors.newSingleThreadExecutor()
+ val firstRenderLatch = CountDownLatch(1)
+ val clearLatch = CountDownLatch(2)
+ var buffer: HardwareBuffer? = null
+ val renderer = SingleBufferedCanvasRendererV34(
+ TEST_WIDTH,
+ TEST_HEIGHT,
+ transformer,
+ executor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<Unit> {
+ override fun render(canvas: Canvas, width: Int, height: Int, param: Unit) {
+ canvas.drawColor(Color.RED)
+ }
+
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ syncFenceCompat?.awaitForever()
+ buffer = hardwareBuffer
+ firstRenderLatch.countDown()
+ clearLatch.countDown()
+ }
+ })
+ try {
+ renderer.render(Unit)
+ firstRenderLatch.await(3000, TimeUnit.MILLISECONDS)
+ renderer.clear()
+ assertTrue(clearLatch.await(3000, TimeUnit.MILLISECONDS))
+ assertNotNull(buffer)
+ val colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)
+ val bitmap = Bitmap.wrapHardwareBuffer(buffer!!, colorSpace)
+ ?.copy(Bitmap.Config.ARGB_8888, false)
+ assertNotNull(bitmap)
+ assertTrue(bitmap!!.isAllColor(Color.TRANSPARENT))
+ } finally {
+ val latch = CountDownLatch(1)
+ renderer.release(true) {
+ executor.shutdownNow()
+ latch.countDown()
+ }
+ assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+ }
+ }
+
+ @Test
+ fun testCancelPending() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val transformer = BufferTransformer().apply {
+ computeTransform(TEST_WIDTH, TEST_HEIGHT, BUFFER_TRANSFORM_IDENTITY)
+ }
+ val executor = Executors.newSingleThreadExecutor()
+ var buffer: HardwareBuffer? = null
+ val initialDrawLatch = CountDownLatch(1)
+
+ var drawCancelledRequestLatch: CountDownLatch? = null
+ val renderer = SingleBufferedCanvasRendererV34(
+ TEST_WIDTH,
+ TEST_HEIGHT,
+ transformer,
+ executor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<Int> {
+ override fun render(canvas: Canvas, width: Int, height: Int, param: Int) {
+ canvas.drawColor(param)
+ }
+
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ syncFenceCompat?.awaitForever()
+ buffer = hardwareBuffer
+ initialDrawLatch.countDown()
+ drawCancelledRequestLatch?.countDown()
+ }
+ })
+ try {
+ renderer.render(Color.RED)
+ assertTrue(initialDrawLatch.await(3000, TimeUnit.MILLISECONDS))
+
+ drawCancelledRequestLatch = CountDownLatch(2)
+ renderer.render(Color.BLUE)
+ renderer.render(Color.BLACK)
+ renderer.cancelPending()
+
+ // Because the requests were cancelled this latch should not be signalled
+ assertFalse(drawCancelledRequestLatch.await(1000, TimeUnit.MILLISECONDS))
+ assertNotNull(buffer)
+ val colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)
+ val bitmap = Bitmap.wrapHardwareBuffer(buffer!!, colorSpace)
+ ?.copy(Bitmap.Config.ARGB_8888, false)
+ assertNotNull(bitmap)
+ assertTrue(bitmap!!.isAllColor(Color.RED))
+ } finally {
+ val latch = CountDownLatch(1)
+ renderer.release(true) {
+ executor.shutdownNow()
+ latch.countDown()
+ }
+ assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+ }
+ }
+
+ @Test
+ fun testMultiReleasesDoesNotCrash() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val transformer = BufferTransformer().apply {
+ computeTransform(TEST_WIDTH, TEST_HEIGHT, BUFFER_TRANSFORM_IDENTITY)
+ }
+ val executor = Executors.newSingleThreadExecutor()
+ val renderer = SingleBufferedCanvasRendererV34(
+ TEST_WIDTH,
+ TEST_HEIGHT,
+ transformer,
+ executor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<Void> {
+ override fun render(canvas: Canvas, width: Int, height: Int, param: Void) {
+ // NO-OP
+ }
+
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ // NO-OP
+ }
+ })
+ try {
+ val latch = CountDownLatch(1)
+ renderer.release(true) {
+ executor.shutdownNow()
+ latch.countDown()
+ }
+ assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+ renderer.release(true)
+ } finally {
+ if (!executor.isShutdown) {
+ executor.shutdownNow()
+ }
+ }
+ }
+
+ @Test
+ fun testRendererVisibleFlag() {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ var supportsNativeAndroidFence = false
+ withEgl { eglManager ->
+ supportsNativeAndroidFence = eglManager.supportsNativeAndroidFence()
+ }
+ if (!supportsNativeAndroidFence) {
+ return
+ }
+ val transformer = BufferTransformer().apply {
+ computeTransform(TEST_WIDTH, TEST_HEIGHT, BUFFER_TRANSFORM_IDENTITY)
+ }
+ val executor = Executors.newSingleThreadExecutor()
+ var syncFenceNull = false
+ var drawLatch: CountDownLatch? = null
+ val renderer = SingleBufferedCanvasRendererV34(
+ TEST_WIDTH,
+ TEST_HEIGHT,
+ transformer,
+ executor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<Int> {
+ override fun render(canvas: Canvas, width: Int, height: Int, param: Int) {
+ canvas.drawColor(param)
+ }
+
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ syncFenceNull = syncFenceCompat == null
+ syncFenceCompat?.awaitForever()
+ drawLatch?.countDown()
+ }
+ })
+ try {
+ renderer.isVisible = false
+ drawLatch = CountDownLatch(1)
+ renderer.render(Color.RED)
+ assertTrue(drawLatch.await(3000, TimeUnit.MILLISECONDS))
+ assertFalse(syncFenceNull)
+
+ renderer.isVisible = true
+ drawLatch = CountDownLatch(1)
+ renderer.render(Color.BLUE)
+ assertTrue(drawLatch.await(3000, TimeUnit.MILLISECONDS))
+ } finally {
+ val latch = CountDownLatch(1)
+ renderer.release(true) {
+ executor.shutdownNow()
+ latch.countDown()
+ }
+ assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+ }
+ }
+
+ private fun testRenderWithTransform(
+ transform: Int,
+ actualColors: RectColors,
+ expectedColors: RectColors
+ ) {
+ if (!BuildCompat.isAtLeastU()) {
+ return
+ }
+ val transformer = BufferTransformer()
+ transformer.computeTransform(TEST_WIDTH, TEST_HEIGHT, transform)
+ val executor = Executors.newSingleThreadExecutor()
+ var buffer: HardwareBuffer? = null
+ val renderLatch = CountDownLatch(1)
+ val renderer = SingleBufferedCanvasRendererV34(
+ TEST_WIDTH,
+ TEST_HEIGHT,
+ transformer,
+ executor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<Int> {
+ override fun render(canvas: Canvas, width: Int, height: Int, param: Int) {
+ drawSquares(
+ canvas,
+ width,
+ height,
+ actualColors.topLeft,
+ actualColors.topRight,
+ actualColors.bottomLeft,
+ actualColors.bottomRight
+ )
+ }
+
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ syncFenceCompat?.awaitForever()
+ buffer = hardwareBuffer
+ renderLatch.countDown()
+ }
+ })
+ try {
+ renderer.render(0)
+ assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+ assertNotNull(buffer)
+ val colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)
+ val bitmap = Bitmap.wrapHardwareBuffer(buffer!!, colorSpace)
+ ?.copy(Bitmap.Config.ARGB_8888, false)
+ assertNotNull(bitmap)
+ bitmap!!.verifyQuadrants(
+ expectedColors.topLeft,
+ expectedColors.topRight,
+ expectedColors.bottomLeft,
+ expectedColors.bottomRight
+ )
+ } finally {
+ val latch = CountDownLatch(1)
+ renderer.release(true) {
+ executor.shutdownNow()
+ latch.countDown()
+ }
+ assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+ }
+ }
+}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlCompatTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlCompatTest.kt
index 4d9d4b8..e4715d6 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlCompatTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlCompatTest.kt
@@ -16,19 +16,23 @@
package androidx.graphics.surface
+import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.ColorSpace
import android.graphics.Rect
import android.graphics.Region
+import android.hardware.DataSpace
import android.opengl.EGL14
import android.os.Build
import android.os.SystemClock
+import android.view.Display
import android.view.SurfaceHolder
import androidx.graphics.opengl.egl.EGLConfigAttributes
import androidx.graphics.opengl.egl.EGLManager
import androidx.graphics.opengl.egl.EGLSpec
import androidx.graphics.opengl.egl.EGLVersion
import androidx.graphics.opengl.egl.supportsNativeAndroidFence
+import androidx.graphics.surface.SurfaceControlUtils.Companion.getSolidBuffer
import androidx.hardware.SyncFenceCompat
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
@@ -40,9 +44,11 @@
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Before
@@ -1891,6 +1897,165 @@
}
}
+ @SuppressLint("NewApi")
+ @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun testSetExtendedRangeBrightnessThrowsOnUnsupportedPlatforms() {
+ ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
+ .moveToState(
+ Lifecycle.State.CREATED
+ ).onActivity {
+ val callback = object : SurfaceHolderCallback() {
+ override fun surfaceCreated(sh: SurfaceHolder) {
+
+ assertThrows(UnsupportedOperationException::class.java) {
+ val surfaceControl = SurfaceControlCompat.Builder()
+ .setName("testSurfaceControl")
+ .setParent(it.mSurfaceView)
+ .build()
+ SurfaceControlCompat.Transaction()
+ .setExtendedRangeBrightness(surfaceControl, 1.0f, 2.0f)
+ .commit()
+ }
+ }
+ }
+
+ it.addSurface(it.mSurfaceView, callback)
+ }
+ }
+
+ @SuppressLint("NewApi")
+ @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.S_V2)
+ @Test
+ fun testSetDataSpaceThrowsOnUnsupportedPlatforms() {
+ ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
+ .moveToState(
+ Lifecycle.State.CREATED
+ ).onActivity {
+ val callback = object : SurfaceHolderCallback() {
+ override fun surfaceCreated(sh: SurfaceHolder) {
+
+ assertThrows(UnsupportedOperationException::class.java) {
+ val surfaceControl = SurfaceControlCompat.Builder()
+ .setName("testSurfaceControl")
+ .setParent(it.mSurfaceView)
+ .build()
+
+ val extendedDataspace = DataSpace.pack(
+ DataSpace.STANDARD_BT709,
+ DataSpace.TRANSFER_SRGB, DataSpace.RANGE_EXTENDED
+ )
+ SurfaceControlCompat.Transaction()
+ .setDataSpace(surfaceControl, extendedDataspace)
+ .commit()
+ }
+ }
+ }
+
+ it.addSurface(it.mSurfaceView, callback)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testSetExtendedRangeBrightness() {
+ val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
+ .moveToState(
+ Lifecycle.State.CREATED
+ ).onActivity {
+ val display = it.display
+ assertNotNull(display)
+ if (display!!.isHdrSdrRatioAvailable) {
+ assertEquals(1.0f, display.hdrSdrRatio, .0001f)
+ }
+
+ it.window.attributes.screenBrightness = 0.01f
+ val hdrReady = CountDownLatch(1)
+ val listenerErrors = arrayOfNulls<Exception>(1)
+ if (display.isHdrSdrRatioAvailable) {
+ display.registerHdrSdrRatioChangedListener(
+ executor!!,
+ object : Consumer<Display?> {
+ var mIsRegistered = true
+ override fun accept(updatedDisplay: Display?) {
+ try {
+ assertEquals(display.displayId, updatedDisplay!!.displayId)
+ assertTrue(mIsRegistered)
+ if (display.hdrSdrRatio > 2f) {
+ hdrReady.countDown()
+ display.unregisterHdrSdrRatioChangedListener(this)
+ mIsRegistered = false
+ }
+ } catch (e: Exception) {
+ synchronized(it) {
+ listenerErrors[0] = e
+ hdrReady.countDown()
+ }
+ }
+ }
+ })
+ } else {
+ assertThrows(IllegalStateException::class.java) {
+ display.registerHdrSdrRatioChangedListener(
+ executor!!,
+ Consumer { _: Display? -> })
+ }
+ }
+ val extendedDataspace = DataSpace.pack(DataSpace.STANDARD_BT709,
+ DataSpace.TRANSFER_SRGB, DataSpace.RANGE_EXTENDED)
+ val buffer = getSolidBuffer(
+ SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
+ SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
+ Color.RED)
+ val callback = object : SurfaceHolderCallback() {
+ override fun surfaceCreated(sh: SurfaceHolder) {
+ val scCompat = SurfaceControlCompat
+ .Builder()
+ .setParent(it.getSurfaceView())
+ .setName("SurfaceControlCompatTest")
+ .build()
+
+ SurfaceControlCompat.Transaction()
+ .setBuffer(scCompat, buffer)
+ .setDataSpace(scCompat, extendedDataspace)
+ .setExtendedRangeBrightness(scCompat, 1.0f, 3.0f)
+ .setVisibility(scCompat, true)
+ .commit()
+ }
+ }
+
+ it.addSurface(it.mSurfaceView, callback)
+ }
+
+ scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+ SurfaceControlUtils.validateOutput(it.window) { bitmap ->
+ val coord = intArrayOf(0, 0)
+ it.mSurfaceView.getLocationInWindow(coord)
+ val topLeft = bitmap.getPixel(
+ coord[0] + 2,
+ coord[1] + 2
+ )
+ val topRight = bitmap.getPixel(
+ coord[0] + it.mSurfaceView.width - 2,
+ coord[1] + 2
+ )
+ val bottomLeft = bitmap.getPixel(
+ coord[0] + 2,
+ coord[1] + it.mSurfaceView.height - 2
+ )
+ val bottomRight = bitmap.getPixel(
+ coord[0] + it.mSurfaceView.width - 2,
+ coord[1] + it.mSurfaceView.height - 2
+ )
+
+ Color.RED == topLeft &&
+ topLeft == topRight &&
+ bottomLeft == topRight &&
+ bottomLeft == bottomRight
+ }
+ }
+ }
+
fun Color.compositeOver(background: Color): Color {
val fg = this.convert(background.colorSpace)
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
index 91a79e35..ead3b1d 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
@@ -20,7 +20,10 @@
import android.graphics.Bitmap
import android.graphics.Color
import android.hardware.HardwareBuffer
+import android.os.Build
import android.os.SystemClock
+import android.view.Window
+import androidx.annotation.RequiresApi
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert
@@ -28,6 +31,18 @@
@SdkSuppress(minSdkVersion = 29)
internal class SurfaceControlUtils {
companion object {
+
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun validateOutput(window: Window, block: (bitmap: Bitmap) -> Boolean) {
+ val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+ val bitmap = uiAutomation.takeScreenshot(window)
+ if (bitmap != null) {
+ block(bitmap)
+ } else {
+ throw IllegalArgumentException("Unable to obtain bitmap from screenshot")
+ }
+ }
+
fun validateOutput(block: (bitmap: Bitmap) -> Boolean) {
var sleepDurationMillis = 1000L
var success = false
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRenderer.kt
index 8a66107..ffc799b 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRenderer.kt
@@ -19,8 +19,10 @@
import android.graphics.Canvas
import android.hardware.HardwareBuffer
import android.os.Build
+import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
+import androidx.core.os.BuildCompat
import androidx.hardware.SyncFenceCompat
import java.util.concurrent.Executor
@@ -28,6 +30,7 @@
* Interface to provide an abstraction around implementations for a low latency hardware
* accelerated [Canvas] that provides a [HardwareBuffer] with the [Canvas] rendered scene
*/
+@RequiresApi(Build.VERSION_CODES.Q)
internal interface SingleBufferedCanvasRenderer<T> {
interface RenderCallbacks<T> {
@@ -67,7 +70,7 @@
companion object {
- @RequiresApi(Build.VERSION_CODES.Q)
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
fun <T> create(
width: Int,
height: Int,
@@ -75,14 +78,23 @@
executor: Executor,
bufferReadyListener: RenderCallbacks<T>
): SingleBufferedCanvasRenderer<T> {
- // TODO return different instance for corresponding platform version
- return SingleBufferedCanvasRendererV29(
- width,
- height,
- bufferTransformer,
- executor,
- bufferReadyListener
- )
+ return if (BuildCompat.isAtLeastU()) {
+ SingleBufferedCanvasRendererV34(
+ width,
+ height,
+ bufferTransformer,
+ executor,
+ bufferReadyListener
+ )
+ } else {
+ SingleBufferedCanvasRendererV29(
+ width,
+ height,
+ bufferTransformer,
+ executor,
+ bufferReadyListener
+ )
+ }
}
}
}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34.kt
new file mode 100644
index 0000000..114a8c2
--- /dev/null
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV34.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.lowlatency
+
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.HardwareBufferRenderer
+import android.graphics.RenderNode
+import android.hardware.HardwareBuffer
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.SystemClock
+import androidx.annotation.RequiresApi
+import androidx.hardware.SyncFenceCompat
+import java.util.concurrent.Executor
+
+@RequiresApi(34)
+internal class SingleBufferedCanvasRendererV34<T>(
+ private val width: Int,
+ private val height: Int,
+ private val bufferTransformer: BufferTransformer,
+ private val executor: Executor,
+ private val callbacks: SingleBufferedCanvasRenderer.RenderCallbacks<T>
+) : SingleBufferedCanvasRenderer<T> {
+
+ private val mRenderNode = RenderNode("node").apply {
+ setPosition(
+ 0,
+ 0,
+ width,
+ height
+ )
+ clipToBounds = false
+ }
+
+ private val mInverseTransform =
+ bufferTransformer.invertBufferTransform(bufferTransformer.computedTransform)
+ private val mHandlerThread = HandlerThread("renderRequestThread").apply { start() }
+ private val mHandler = Handler(mHandlerThread.looper)
+
+ private inline fun dispatchOnExecutor(crossinline block: () -> Unit) {
+ executor.execute {
+ block()
+ }
+ }
+
+ private inline fun doRender(block: (Canvas) -> Unit) {
+ val canvas = mRenderNode.beginRecording()
+ block(canvas)
+ mRenderNode.endRecording()
+
+ mHardwareBufferRenderer.obtainRenderRequest().apply {
+ if (mInverseTransform != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+ setBufferTransform(mInverseTransform)
+ }
+ draw(executor) { result ->
+ callbacks.onBufferReady(mHardwareBuffer, SyncFenceCompat(result.fence))
+ }
+ }
+ }
+
+ private fun tearDown() {
+ mHardwareBufferRenderer.close()
+ mHandlerThread.quit()
+ }
+
+ private val mHardwareBuffer = HardwareBuffer.create(
+ bufferTransformer.glWidth,
+ bufferTransformer.glHeight,
+ HardwareBuffer.RGBA_8888,
+ 1,
+ FrontBufferUtils.obtainHardwareBufferUsageFlags()
+ )
+
+ private val mHardwareBufferRenderer = HardwareBufferRenderer(mHardwareBuffer).apply {
+ setContentRoot(mRenderNode)
+ }
+
+ private var mIsReleasing = false
+
+ override fun render(param: T) {
+ if (!mIsReleasing) {
+ mHandler.post(RENDER) {
+ dispatchOnExecutor {
+ doRender { canvas ->
+ callbacks.render(canvas, width, height, param)
+ }
+ }
+ }
+ }
+ }
+
+ override var isVisible: Boolean = false
+
+ override fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)?) {
+ if (!mIsReleasing) {
+ if (cancelPending) {
+ cancelPending()
+ }
+ mHandler.post(RELEASE) {
+ tearDown()
+ if (onReleaseComplete != null) {
+ dispatchOnExecutor {
+ onReleaseComplete.invoke()
+ }
+ }
+ }
+ mIsReleasing = true
+ }
+ }
+
+ override fun clear() {
+ if (!mIsReleasing) {
+ mHandler.post(CLEAR) {
+ dispatchOnExecutor {
+ doRender { canvas ->
+ canvas.drawColor(Color.BLACK, BlendMode.CLEAR)
+ }
+ }
+ }
+ }
+ }
+
+ override fun cancelPending() {
+ if (!mIsReleasing) {
+ mHandler.removeCallbacksAndMessages(CLEAR)
+ mHandler.removeCallbacksAndMessages(RENDER)
+ }
+ }
+
+ private companion object {
+ const val RENDER = 0
+ const val CLEAR = 1
+ const val RELEASE = 2
+ }
+
+ /**
+ * Handler does not expose a post method that takes a token and a runnable.
+ * We need the token to be able to cancel pending requests so just call
+ * postAtTime with the default of SystemClock.uptimeMillis
+ */
+ private fun Handler.post(token: Any?, runnable: Runnable) {
+ postAtTime(runnable, token, SystemClock.uptimeMillis())
+ }
+}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
index daa5636..77ba452 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
@@ -18,12 +18,14 @@
import android.graphics.Rect
import android.graphics.Region
+import android.hardware.DataSpace
import android.hardware.HardwareBuffer
import android.os.Build
import android.view.AttachedSurfaceControl
import android.view.Surface
import android.view.SurfaceControl
import android.view.SurfaceView
+import androidx.annotation.FloatRange
import androidx.annotation.IntDef
import androidx.annotation.RequiresApi
import androidx.graphics.lowlatency.FrontBufferUtils
@@ -476,6 +478,79 @@
}
/**
+ * Sets the desired extended range brightness for the layer. This only applies for layers
+ * that are displaying [HardwareBuffer] instances with a DataSpace of
+ * [DataSpace.RANGE_EXTENDED].
+ *
+ * @param surfaceControl The layer whose extended range brightness is being specified
+ * @param currentBufferRatio The current hdr/sdr ratio of the current buffer. For example
+ * if the buffer was rendered with a target SDR whitepoint of 100 nits and a max display
+ * brightness of 200 nits, this should be set to 2.0f.
+ *
+ * Default value is 1.0f.
+ *
+ * Transfer functions that encode their own brightness ranges,
+ * such as HLG or PQ, should also set this to 1.0f and instead
+ * communicate extended content brightness information via
+ * metadata such as CTA861_3 or SMPTE2086.
+ *
+ * Must be finite && >= 1.0f
+ *
+ * @param desiredRatio The desired hdr/sdr ratio. This can be used to communicate the max
+ * desired brightness range. This is similar to the "max luminance" value in other HDR
+ * metadata formats, but represented as a ratio of the target SDR whitepoint to the max
+ * display brightness. The system may not be able to, or may choose not to, deliver the
+ * requested range.
+ *
+ * While requesting a large desired ratio will result in the most
+ * dynamic range, voluntarily reducing the requested range can help
+ * improve battery life as well as can improve quality by ensuring
+ * greater bit depth is allocated to the luminance range in use.
+ *
+ * Default value is 1.0f and indicates that extended range brightness
+ * is not being used, so the resulting SDR or HDR behavior will be
+ * determined entirely by the dataspace being used (ie, typically SDR
+ * however PQ or HLG transfer functions will still result in HDR)
+ *
+ * Must be finite && >= 1.0f
+ * @return this
+ **/
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setExtendedRangeBrightness(
+ surfaceControl: SurfaceControlCompat,
+ @FloatRange(from = 1.0, fromInclusive = true) currentBufferRatio: Float,
+ @FloatRange(from = 1.0, fromInclusive = true) desiredRatio: Float
+ ): Transaction {
+ mImpl.setExtendedRangeBrightness(
+ surfaceControl.scImpl,
+ currentBufferRatio,
+ desiredRatio
+ )
+ return this
+ }
+
+ /**
+ * Set the dataspace for the SurfaceControl. This will control how the buffer
+ * set with [setBuffer] is displayed.
+ *
+ * @param surfaceControl The SurfaceControl to update
+ * @param dataSpace The dataspace to set it to. Must be one of named
+ * [android.hardware.DataSpace] types.
+ *
+ * @see [android.view.SurfaceControl.Transaction.setDataSpace]
+ *
+ * @return this
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ fun setDataSpace(
+ surfaceControl: SurfaceControlCompat,
+ dataSpace: Int
+ ): Transaction {
+ mImpl.setDataSpace(surfaceControl.scImpl, dataSpace)
+ return this
+ }
+
+ /**
* Commit the transaction, clearing it's state, and making it usable as a new transaction.
* This will not release any resources and [SurfaceControlCompat.Transaction.close] must be
* called to release the transaction.
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlImpl.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlImpl.kt
index 460f50a..d236c1e 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlImpl.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlImpl.kt
@@ -310,6 +310,25 @@
): Transaction
/**
+ * See [SurfaceControlCompat.Transaction.setExtendedRangeBrightness]
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun setExtendedRangeBrightness(
+ surfaceControl: SurfaceControlImpl,
+ currentBufferRatio: Float,
+ desiredRatio: Float
+ ): Transaction
+
+ /**
+ * See [SurfaceControlCompat.Transaction.setDataSpace]
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ fun setDataSpace(
+ surfaceControl: SurfaceControlImpl,
+ dataSpace: Int
+ ): Transaction
+
+ /**
* Commit the transaction, clearing it's state, and making it usable as a new transaction.
* This will not release any resources and [SurfaceControlImpl.Transaction.close] must be
* called to release the transaction.
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV29.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV29.kt
index 8a303de..c9f06f1 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV29.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV29.kt
@@ -361,6 +361,33 @@
}
/**
+ * See [SurfaceControlCompat.Transaction.setExtendedRangeBrightness]
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ override fun setExtendedRangeBrightness(
+ surfaceControl: SurfaceControlImpl,
+ currentBufferRatio: Float,
+ desiredRatio: Float
+ ): SurfaceControlImpl.Transaction {
+ throw UnsupportedOperationException(
+ "Configuring the extended range brightness is only available on Android U+"
+ )
+ }
+
+ /**
+ * See [SurfaceControlCompat.Transaction.setDataSpace]
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ override fun setDataSpace(
+ surfaceControl: SurfaceControlImpl,
+ dataSpace: Int
+ ): SurfaceControlImpl.Transaction {
+ throw UnsupportedOperationException(
+ "Configuring the data space is only available on Android T+"
+ )
+ }
+
+ /**
* See [SurfaceControlWrapper.Transaction.close]
*/
override fun close() {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
index 37311f5..2a9e3b0 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
@@ -23,6 +23,7 @@
import android.os.Build
import android.view.AttachedSurfaceControl
import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
import android.view.SurfaceView
import androidx.annotation.RequiresApi
import androidx.hardware.SyncFenceImpl
@@ -259,6 +260,52 @@
}
/**
+ * See [SurfaceControlCompat.Transaction.setExtendedRangeBrightness]
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ override fun setExtendedRangeBrightness(
+ surfaceControl: SurfaceControlImpl,
+ currentBufferRatio: Float,
+ desiredRatio: Float
+ ): SurfaceControlImpl.Transaction {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ SurfaceControlTransactionVerificationHelperV34.setExtendedRangeBrightness(
+ mTransaction,
+ surfaceControl.asFrameworkSurfaceControl(),
+ currentBufferRatio,
+ desiredRatio
+ )
+ return this
+ } else {
+ throw UnsupportedOperationException(
+ "Configuring the extended range brightness is only available on Android U+"
+ )
+ }
+ }
+
+ /**
+ * See [SurfaceControlCompat.Transaction.setDataSpace]
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ override fun setDataSpace(
+ surfaceControl: SurfaceControlImpl,
+ dataSpace: Int
+ ): SurfaceControlImpl.Transaction {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ SurfaceControlTransactionVerificationHelperV33.setDataSpace(
+ mTransaction,
+ surfaceControl.asFrameworkSurfaceControl(),
+ dataSpace
+ )
+ } else {
+ throw UnsupportedOperationException(
+ "Configuring the data space is only available on Android T+"
+ )
+ }
+ return this
+ }
+
+ /**
* See [SurfaceControlImpl.Transaction.commit]
*/
override fun commit() {
@@ -296,4 +343,27 @@
throw IllegalArgumentException("Parent implementation is not for Android T")
}
}
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private object SurfaceControlTransactionVerificationHelperV34 {
+
+ @androidx.annotation.DoNotInline
+ fun setExtendedRangeBrightness(
+ transaction: Transaction,
+ surfaceControl: SurfaceControl,
+ currentBufferRatio: Float,
+ desiredRatio: Float
+ ) {
+ transaction.setExtendedRangeBrightness(surfaceControl, currentBufferRatio, desiredRatio)
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+private object SurfaceControlTransactionVerificationHelperV33 {
+
+ @androidx.annotation.DoNotInline
+ fun setDataSpace(transaction: Transaction, surfaceControl: SurfaceControl, dataspace: Int) {
+ transaction.setDataSpace(surfaceControl, dataspace)
+ }
}
\ No newline at end of file
diff --git a/graphics/graphics-path/api/current.txt b/graphics/graphics-path/api/current.txt
new file mode 100644
index 0000000..902f803
--- /dev/null
+++ b/graphics/graphics-path/api/current.txt
@@ -0,0 +1,61 @@
+// Signature format: 4.0
+package androidx.graphics.path {
+
+ @androidx.core.os.BuildCompat.PrereleaseSdkCheck public final class PathIterator implements java.util.Iterator<androidx.graphics.path.PathSegment> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public PathIterator(android.graphics.Path path, optional androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation, optional float tolerance);
+ method public int calculateSize(optional boolean includeConvertedConics);
+ method public androidx.graphics.path.PathIterator.ConicEvaluation getConicEvaluation();
+ method public android.graphics.Path getPath();
+ method public float getTolerance();
+ method public boolean hasNext();
+ method public androidx.graphics.path.PathSegment next();
+ method public androidx.graphics.path.PathSegment.Type next(float[] points);
+ method public androidx.graphics.path.PathSegment.Type next(float[] points, optional int offset);
+ method public androidx.graphics.path.PathSegment.Type peek();
+ property public final androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation;
+ property public final android.graphics.Path path;
+ property public final float tolerance;
+ }
+
+ public enum PathIterator.ConicEvaluation {
+ method public static androidx.graphics.path.PathIterator.ConicEvaluation valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.graphics.path.PathIterator.ConicEvaluation[] values();
+ enum_constant public static final androidx.graphics.path.PathIterator.ConicEvaluation AsConic;
+ enum_constant public static final androidx.graphics.path.PathIterator.ConicEvaluation AsQuadratics;
+ }
+
+ public final class PathSegment {
+ method public android.graphics.PointF![] getPoints();
+ method public androidx.graphics.path.PathSegment.Type getType();
+ method public float getWeight();
+ property public final android.graphics.PointF![] points;
+ property public final androidx.graphics.path.PathSegment.Type type;
+ property public final float weight;
+ }
+
+ public enum PathSegment.Type {
+ method public static androidx.graphics.path.PathSegment.Type valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.graphics.path.PathSegment.Type[] values();
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Close;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Conic;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Cubic;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Done;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Line;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Move;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Quadratic;
+ }
+
+ public final class PathSegmentUtilities {
+ method public static androidx.graphics.path.PathSegment getCloseSegment();
+ method public static androidx.graphics.path.PathSegment getDoneSegment();
+ property public static final androidx.graphics.path.PathSegment CloseSegment;
+ property public static final androidx.graphics.path.PathSegment DoneSegment;
+ }
+
+ public final class PathUtilities {
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static operator androidx.graphics.path.PathIterator iterator(android.graphics.Path);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static androidx.graphics.path.PathIterator iterator(android.graphics.Path, androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation, optional float tolerance);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/graphics/graphics-path/api/res-current.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to graphics/graphics-path/api/res-current.txt
diff --git a/graphics/graphics-path/api/restricted_current.txt b/graphics/graphics-path/api/restricted_current.txt
new file mode 100644
index 0000000..902f803
--- /dev/null
+++ b/graphics/graphics-path/api/restricted_current.txt
@@ -0,0 +1,61 @@
+// Signature format: 4.0
+package androidx.graphics.path {
+
+ @androidx.core.os.BuildCompat.PrereleaseSdkCheck public final class PathIterator implements java.util.Iterator<androidx.graphics.path.PathSegment> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public PathIterator(android.graphics.Path path, optional androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation, optional float tolerance);
+ method public int calculateSize(optional boolean includeConvertedConics);
+ method public androidx.graphics.path.PathIterator.ConicEvaluation getConicEvaluation();
+ method public android.graphics.Path getPath();
+ method public float getTolerance();
+ method public boolean hasNext();
+ method public androidx.graphics.path.PathSegment next();
+ method public androidx.graphics.path.PathSegment.Type next(float[] points);
+ method public androidx.graphics.path.PathSegment.Type next(float[] points, optional int offset);
+ method public androidx.graphics.path.PathSegment.Type peek();
+ property public final androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation;
+ property public final android.graphics.Path path;
+ property public final float tolerance;
+ }
+
+ public enum PathIterator.ConicEvaluation {
+ method public static androidx.graphics.path.PathIterator.ConicEvaluation valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.graphics.path.PathIterator.ConicEvaluation[] values();
+ enum_constant public static final androidx.graphics.path.PathIterator.ConicEvaluation AsConic;
+ enum_constant public static final androidx.graphics.path.PathIterator.ConicEvaluation AsQuadratics;
+ }
+
+ public final class PathSegment {
+ method public android.graphics.PointF![] getPoints();
+ method public androidx.graphics.path.PathSegment.Type getType();
+ method public float getWeight();
+ property public final android.graphics.PointF![] points;
+ property public final androidx.graphics.path.PathSegment.Type type;
+ property public final float weight;
+ }
+
+ public enum PathSegment.Type {
+ method public static androidx.graphics.path.PathSegment.Type valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.graphics.path.PathSegment.Type[] values();
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Close;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Conic;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Cubic;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Done;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Line;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Move;
+ enum_constant public static final androidx.graphics.path.PathSegment.Type Quadratic;
+ }
+
+ public final class PathSegmentUtilities {
+ method public static androidx.graphics.path.PathSegment getCloseSegment();
+ method public static androidx.graphics.path.PathSegment getDoneSegment();
+ property public static final androidx.graphics.path.PathSegment CloseSegment;
+ property public static final androidx.graphics.path.PathSegment DoneSegment;
+ }
+
+ public final class PathUtilities {
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static operator androidx.graphics.path.PathIterator iterator(android.graphics.Path);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static androidx.graphics.path.PathIterator iterator(android.graphics.Path, androidx.graphics.path.PathIterator.ConicEvaluation conicEvaluation, optional float tolerance);
+ }
+
+}
+
diff --git a/graphics/graphics-path/build.gradle b/graphics/graphics-path/build.gradle
new file mode 100644
index 0000000..392c246
--- /dev/null
+++ b/graphics/graphics-path/build.gradle
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+dependencies {
+ api(libs.kotlinStdlib)
+
+ implementation('androidx.appcompat:appcompat:1.6.1')
+ implementation(project(':core:core'))
+
+ androidTestImplementation("androidx.annotation:annotation:1.4.0")
+ androidTestImplementation("androidx.core:core-ktx:1.8.0")
+ androidTestImplementation("androidx.test:core:1.4.0@aar")
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.truth)
+}
+
+android {
+ namespace "androidx.graphics.path"
+
+ defaultConfig {
+ minSdkVersion 21 // Limited to 21+ due to native changes before that release
+ externalNativeBuild {
+ cmake {
+ cppFlags.addAll(
+ [
+ "-std=c++17",
+ "-Wno-unused-command-line-argument",
+ "-Wl,--hash-style=both", // Required to support API levels below 23
+ "-fno-stack-protector",
+ "-fno-exceptions",
+ "-fno-unwind-tables",
+ "-fno-asynchronous-unwind-tables",
+ "-fno-rtti",
+ "-ffast-math",
+ "-ffp-contract=fast",
+ "-fvisibility-inlines-hidden",
+ "-fvisibility=hidden",
+ "-fomit-frame-pointer",
+ "-ffunction-sections",
+ "-fdata-sections",
+ "-Wl,--gc-sections",
+ "-Wl,-Bsymbolic-functions",
+ ])
+ }
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path file('src/main/cpp/CMakeLists.txt')
+ version libs.versions.cmake.get()
+ }
+ }
+
+}
+
+androidx {
+ name = "Android Graphics Path"
+ type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GRAPHICS_PATH
+ inceptionYear = "2022"
+ description = "Query segment data for android.graphics.Path objects"
+}
diff --git a/graphics/graphics-path/src/androidTest/java/androidx/graphics/path/PathIteratorTest.kt b/graphics/graphics-path/src/androidTest/java/androidx/graphics/path/PathIteratorTest.kt
new file mode 100644
index 0000000..5a58802
--- /dev/null
+++ b/graphics/graphics-path/src/androidTest/java/androidx/graphics/path/PathIteratorTest.kt
@@ -0,0 +1,544 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.path
+
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PointF
+import android.graphics.RectF
+import android.os.Build
+import androidx.core.graphics.applyCanvas
+import androidx.core.graphics.createBitmap
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import kotlin.math.abs
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private fun assertPointsEquals(p1: PointF, p2: PointF) {
+ assertEquals(p1.x, p2.x, 1e-6f)
+ assertEquals(p1.y, p2.y, 1e-6f)
+}
+
+private fun assertPointsEquals(p1: FloatArray, offset: Int, p2: PointF) {
+ assertEquals(p1[0 + offset * 2], p2.x, 1e-6f)
+ assertEquals(p1[1 + offset * 2], p2.y, 1e-6f)
+}
+
+private fun compareBitmaps(b1: Bitmap, b2: Bitmap) {
+ val epsilon: Int
+ if (Build.VERSION.SDK_INT != 23) {
+ epsilon = 1
+ } else {
+ // There is more AA variability between conics and cubics on API 23, leading
+ // to failures on relatively small visual differences. Increase the error
+ // value for just this release to avoid erroneous bitmap comparison failures.
+ epsilon = 32
+ }
+
+ assertEquals(b1.width, b2.width)
+ assertEquals(b1.height, b2.height)
+
+ val p1 = IntArray(b1.width * b1.height)
+ b1.getPixels(p1, 0, b1.width, 0, 0, b1.width, b1.height)
+
+ val p2 = IntArray(b2.width * b2.height)
+ b2.getPixels(p2, 0, b2.width, 0, 0, b2.width, b2.height)
+
+ for (x in 0 until b1.width) {
+ for (y in 0 until b2.width) {
+ val index = y * b1.width + x
+
+ val c1 = p1[index]
+ val c2 = p2[index]
+
+ assertTrue(abs(Color.red(c1) - Color.red(c2)) <= epsilon)
+ assertTrue(abs(Color.green(c1) - Color.green(c2)) <= epsilon)
+ assertTrue(abs(Color.blue(c1) - Color.blue(c2)) <= epsilon)
+ }
+ }
+}
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PathIteratorTest {
+ @Test
+ fun emptyIterator() {
+ val path = Path()
+
+ val iterator = path.iterator()
+ // TODO: un-comment the hasNext() check when the platform has the behavior change
+ // which ignores final DONE ops in the value for hasNext()
+ // assertFalse(iterator.hasNext())
+ val firstSegment = iterator.next()
+ assertEquals(PathSegment.Type.Done, firstSegment.type)
+
+ var count = 0
+ for (segment in path) {
+ // TODO: remove condition check and just increment count when platform change
+ // is checked in which will not iterate when DONE is the only op left
+ if (segment.type != PathSegment.Type.Done) {
+ // Shouldn't get here; count should remain 0
+ count++
+ }
+ }
+
+ assertEquals(0, count)
+ }
+
+ @Test
+ fun emptyPeek() {
+ val path = Path()
+ val iterator = path.iterator()
+ assertEquals(PathSegment.Type.Done, iterator.peek())
+ }
+
+ @Test
+ fun nonEmptyIterator() {
+ val path = Path().apply {
+ moveTo(1.0f, 1.0f)
+ lineTo(2.0f, 2.0f)
+ close()
+ }
+
+ val iterator = path.iterator()
+ assertTrue(iterator.hasNext())
+
+ val types = arrayOf(
+ PathSegment.Type.Move,
+ PathSegment.Type.Line,
+ PathSegment.Type.Close,
+ PathSegment.Type.Done
+ )
+ val points = arrayOf(
+ PointF(1.0f, 1.0f),
+ PointF(2.0f, 2.0f)
+ )
+
+ var count = 0
+ for (segment in path) {
+ assertEquals(types[count], segment.type)
+ when (segment.type) {
+ PathSegment.Type.Move -> {
+ assertEquals(points[count], segment.points[0])
+ }
+ PathSegment.Type.Line -> {
+ assertEquals(points[count - 1], segment.points[0])
+ assertEquals(points[count], segment.points[1])
+ }
+ else -> { }
+ }
+ // TODO: remove condition and just auto-increment count when platform change is
+ // checked in which ignores DONE during iteration
+ if (segment.type != PathSegment.Type.Done) count++
+ }
+
+ assertEquals(3, count)
+ }
+
+ @Test
+ fun peek() {
+ val path = Path().apply {
+ moveTo(1.0f, 1.0f)
+ lineTo(2.0f, 2.0f)
+ close()
+ }
+
+ val iterator = path.iterator()
+ assertEquals(PathSegment.Type.Move, iterator.peek())
+ }
+
+ @Test
+ fun peekBeyond() {
+ val path = Path()
+ assertEquals(PathSegment.Type.Done, path.iterator().peek())
+
+ path.apply {
+ moveTo(1.0f, 1.0f)
+ lineTo(2.0f, 2.0f)
+ close()
+ }
+
+ val iterator = path.iterator()
+ while (iterator.hasNext()) iterator.next()
+ assertEquals(PathSegment.Type.Done, iterator.peek())
+ }
+
+ @Test
+ fun iteratorStyles() {
+ val path = Path().apply {
+ moveTo(1.0f, 1.0f)
+ lineTo(2.0f, 2.0f)
+ cubicTo(3.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f)
+ quadTo(7.0f, 7.0f, 8.0f, 8.0f)
+ moveTo(10.0f, 10.0f)
+ // addRoundRect() will generate conic curves on certain API levels
+ addRoundRect(RectF(12.0f, 12.0f, 36.0f, 36.0f), 8.0f, 8.0f, Path.Direction.CW)
+ close()
+ }
+
+ iteratorStylesImpl(path, PathIterator.ConicEvaluation.AsConic)
+ iteratorStylesImpl(path, PathIterator.ConicEvaluation.AsQuadratics)
+ }
+
+ private fun iteratorStylesImpl(path: Path, conicEvaluation: PathIterator.ConicEvaluation) {
+ val iterator1 = path.iterator(conicEvaluation)
+ val iterator2 = path.iterator(conicEvaluation)
+ val iterator3 = path.iterator(conicEvaluation)
+
+ val points = FloatArray(8)
+ val points2 = FloatArray(16)
+
+ while (iterator1.hasNext() || iterator2.hasNext() || iterator3.hasNext()) {
+ val segment = iterator1.next()
+ val type = iterator2.next(points)
+ val type2 = iterator3.next(points2, 8)
+
+ assertEquals(type, segment.type)
+ assertEquals(type2, segment.type)
+
+ when (type) {
+ PathSegment.Type.Move -> {
+ assertPointsEquals(points, 0, segment.points[0])
+ assertPointsEquals(points2, 4, segment.points[0])
+ }
+
+ PathSegment.Type.Line -> {
+ assertPointsEquals(points, 0, segment.points[0])
+ assertPointsEquals(points, 1, segment.points[1])
+ assertPointsEquals(points2, 4, segment.points[0])
+ assertPointsEquals(points2, 5, segment.points[1])
+ }
+
+ PathSegment.Type.Quadratic -> {
+ assertPointsEquals(points, 0, segment.points[0])
+ assertPointsEquals(points, 1, segment.points[1])
+ assertPointsEquals(points, 2, segment.points[2])
+ assertPointsEquals(points2, 4, segment.points[0])
+ assertPointsEquals(points2, 5, segment.points[1])
+ assertPointsEquals(points2, 6, segment.points[2])
+ }
+
+ PathSegment.Type.Conic -> {
+ assertPointsEquals(points, 0, segment.points[0])
+ assertPointsEquals(points, 1, segment.points[1])
+ assertPointsEquals(points, 2, segment.points[2])
+ // Weight is stored after all of the points
+ assertEquals(points[6], segment.weight)
+
+ assertPointsEquals(points2, 4, segment.points[0])
+ assertPointsEquals(points2, 5, segment.points[1])
+ assertPointsEquals(points2, 6, segment.points[2])
+ // Weight is stored after all of the points
+ assertEquals(points2[14], segment.weight)
+ }
+
+ PathSegment.Type.Cubic -> {
+ assertPointsEquals(points, 0, segment.points[0])
+ assertPointsEquals(points, 1, segment.points[1])
+ assertPointsEquals(points, 2, segment.points[2])
+ assertPointsEquals(points, 3, segment.points[3])
+
+ assertPointsEquals(points2, 4, segment.points[0])
+ assertPointsEquals(points2, 5, segment.points[1])
+ assertPointsEquals(points2, 6, segment.points[2])
+ assertPointsEquals(points2, 7, segment.points[3])
+ }
+
+ PathSegment.Type.Close -> {}
+ PathSegment.Type.Done -> {}
+ }
+ }
+ }
+
+ @Test
+ fun done() {
+ val path = Path().apply {
+ close()
+ }
+
+ val segment = path.iterator().next()
+
+ assertEquals(PathSegment.Type.Done, segment.type)
+ assertEquals(0, segment.points.size)
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun close() {
+ val path = Path().apply {
+ lineTo(10.0f, 12.0f)
+ close()
+ }
+
+ val iterator = path.iterator()
+ // Swallow the move
+ iterator.next()
+ // Swallow the line
+ iterator.next()
+
+ val segment = iterator.next()
+
+ assertEquals(PathSegment.Type.Close, segment.type)
+ assertEquals(0, segment.points.size)
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun moveTo() {
+ val path = Path().apply {
+ moveTo(10.0f, 12.0f)
+ }
+
+ val segment = path.iterator().next()
+
+ assertEquals(PathSegment.Type.Move, segment.type)
+ assertEquals(1, segment.points.size)
+ assertPointsEquals(PointF(10.0f, 12.0f), segment.points[0])
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun lineTo() {
+ val path = Path().apply {
+ moveTo(4.0f, 6.0f)
+ lineTo(10.0f, 12.0f)
+ }
+
+ val iterator = path.iterator()
+ // Swallow the move
+ iterator.next()
+
+ val segment = iterator.next()
+
+ assertEquals(PathSegment.Type.Line, segment.type)
+ assertEquals(2, segment.points.size)
+ assertPointsEquals(PointF(4.0f, 6.0f), segment.points[0])
+ assertPointsEquals(PointF(10.0f, 12.0f), segment.points[1])
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun quadraticTo() {
+ val path = Path().apply {
+ moveTo(4.0f, 6.0f)
+ quadTo(10.0f, 12.0f, 20.0f, 24.0f)
+ }
+
+ val iterator = path.iterator()
+ // Swallow the move
+ iterator.next()
+
+ val segment = iterator.next()
+
+ assertEquals(PathSegment.Type.Quadratic, segment.type)
+ assertEquals(3, segment.points.size)
+ assertPointsEquals(PointF(4.0f, 6.0f), segment.points[0])
+ assertPointsEquals(PointF(10.0f, 12.0f), segment.points[1])
+ assertPointsEquals(PointF(20.0f, 24.0f), segment.points[2])
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun cubicTo() {
+ val path = Path().apply {
+ moveTo(4.0f, 6.0f)
+ cubicTo(10.0f, 12.0f, 20.0f, 24.0f, 30.0f, 36.0f)
+ }
+
+ val iterator = path.iterator()
+ // Swallow the move
+ iterator.next()
+
+ val segment = iterator.next()
+
+ assertEquals(PathSegment.Type.Cubic, segment.type)
+ assertEquals(4, segment.points.size)
+ assertPointsEquals(PointF(4.0f, 6.0f), segment.points[0])
+ assertPointsEquals(PointF(10.0f, 12.0f), segment.points[1])
+ assertPointsEquals(PointF(20.0f, 24.0f), segment.points[2])
+ assertPointsEquals(PointF(30.0f, 36.0f), segment.points[3])
+ assertEquals(0.0f, segment.weight)
+ }
+
+ @Test
+ fun conicTo() {
+ if (Build.VERSION.SDK_INT >= 25) {
+ val path = Path().apply {
+ addRoundRect(RectF(12.0f, 12.0f, 24.0f, 24.0f), 8.0f, 8.0f, Path.Direction.CW)
+ }
+
+ val iterator = path.iterator(PathIterator.ConicEvaluation.AsConic)
+ // Swallow the move
+ iterator.next()
+
+ val segment = iterator.next()
+
+ assertEquals(PathSegment.Type.Conic, segment.type)
+ assertEquals(3, segment.points.size)
+
+ assertPointsEquals(PointF(12.0f, 18.0f), segment.points[0])
+ assertPointsEquals(PointF(12.0f, 12.0f), segment.points[1])
+ assertPointsEquals(PointF(18.0f, 12.0f), segment.points[2])
+ assertEquals(0.70710677f, segment.weight)
+ }
+ }
+
+ @Test
+ fun conicAsQuadratics() {
+ val path = Path().apply {
+ addRoundRect(RectF(12.0f, 12.0f, 24.0f, 24.0f), 8.0f, 8.0f, Path.Direction.CW)
+ }
+
+ for (segment in path) {
+ if (segment.type == PathSegment.Type.Conic) fail("Found conic, none expected: $segment")
+ }
+ }
+
+ @Test
+ fun convertedConics() {
+ val path1 = Path().apply {
+ addRoundRect(RectF(12.0f, 12.0f, 64.0f, 64.0f), 12.0f, 12.0f, Path.Direction.CW)
+ }
+
+ val path2 = Path()
+ for (segment in path1) {
+ when (segment.type) {
+ PathSegment.Type.Move -> path2.moveTo(segment.points[0].x, segment.points[0].y)
+ PathSegment.Type.Line -> path2.lineTo(segment.points[1].x, segment.points[1].y)
+ PathSegment.Type.Quadratic -> path2.quadTo(
+ segment.points[1].x, segment.points[1].y,
+ segment.points[2].x, segment.points[2].y
+ )
+ PathSegment.Type.Conic -> fail("Unexpected conic! $segment")
+ PathSegment.Type.Cubic -> path2.cubicTo(
+ segment.points[1].x, segment.points[1].y,
+ segment.points[2].x, segment.points[2].y,
+ segment.points[3].x, segment.points[3].y
+ )
+ PathSegment.Type.Close -> path2.close()
+ PathSegment.Type.Done -> { }
+ }
+ }
+
+ // Now with smaller error tolerance
+ val path3 = Path()
+ for (segment in path1.iterator(
+ conicEvaluation = PathIterator.ConicEvaluation.AsQuadratics,
+ .001f
+ )) {
+ when (segment.type) {
+ PathSegment.Type.Move -> path3.moveTo(segment.points[0].x, segment.points[0].y)
+ PathSegment.Type.Line -> path3.lineTo(segment.points[1].x, segment.points[1].y)
+ PathSegment.Type.Quadratic -> path3.quadTo(
+ segment.points[1].x, segment.points[1].y,
+ segment.points[2].x, segment.points[2].y
+ )
+ PathSegment.Type.Conic -> fail("Unexpected conic! $segment")
+ PathSegment.Type.Cubic -> path3.cubicTo(
+ segment.points[1].x, segment.points[1].y,
+ segment.points[2].x, segment.points[2].y,
+ segment.points[3].x, segment.points[3].y
+ )
+ PathSegment.Type.Close -> path3.close()
+ PathSegment.Type.Done -> { }
+ }
+ }
+
+ val b1 = createBitmap(76, 76).applyCanvas {
+ drawARGB(255, 255, 255, 255)
+ drawPath(path1, Paint().apply {
+ color = argb(1.0f, 0.0f, 0.0f, 1.0f)
+ strokeWidth = 2.0f
+ isAntiAlias = true
+ style = Paint.Style.STROKE
+ })
+ }
+
+ val b2 = createBitmap(76, 76).applyCanvas {
+ drawARGB(255, 255, 255, 255)
+ drawPath(path2, Paint().apply {
+ color = argb(1.0f, 0.0f, 0.0f, 1.0f)
+ strokeWidth = 2.0f
+ isAntiAlias = true
+ style = Paint.Style.STROKE
+ })
+ }
+
+ compareBitmaps(b1, b2)
+ // Note: b1-vs-b3 is not a valid comparison; default Skia rendering does not use an
+ // error tolerance that low. The test for fine-precision in path3 was just to
+ // ensure that the system could handle the extra data and operations required
+ }
+
+ @Test
+ fun sizes() {
+ val path = Path()
+ var iterator: PathIterator = path.iterator()
+
+ if (iterator.calculateSize() > 0) {
+ assertEquals(PathSegment.Type.Done, iterator.peek())
+ }
+ // TODO: replace above check with below assertEquals after platform change is checked
+ // in which returns a size of zero when there the only op in the path is DONE
+ // assertEquals(0, iterator.size())
+
+ path.addRoundRect(RectF(12.0f, 12.0f, 64.0f, 64.0f), 8.0f, 8.0f,
+ Path.Direction.CW)
+
+ // Skia converted
+ if (Build.VERSION.SDK_INT > 22) {
+ // Preserve conics and count
+ iterator = path.iterator(PathIterator.ConicEvaluation.AsConic)
+ assert(iterator.calculateSize() == 10 || iterator.calculateSize() == 11)
+ // TODO: replace assert() above with assertEquals below once platform change exists
+ // which does not count final DONE in the size
+ // assertEquals(10, iterator.size())
+ assertEquals(iterator.calculateSize(true), iterator.calculateSize())
+ }
+
+ // Convert conics and count
+ iterator = path.iterator(PathIterator.ConicEvaluation.AsQuadratics)
+ if (Build.VERSION.SDK_INT > 22) {
+ // simple size, not including conic conversion
+ assert(iterator.calculateSize(false) == 10 || iterator.calculateSize(false) == 11)
+ // TODO: replace assert() above with assertEquals below once platform change exists
+ // which does not count final DONE in the size
+ // assertEquals(10, iterator.size(false))
+ } else {
+ // round rects pre-API22 used line/quad/quad for each corner
+ assertEquals(14, iterator.calculateSize())
+ }
+ // now get the size with converted conics
+ val size = iterator.calculateSize()
+ assert(size == 14 || size == 15)
+ // TODO: replace assert() above with assertEquals below once platform change exists
+ // which does not count final DONE in the size
+ // assertEquals(14, iterator.size())
+ }
+}
+
+fun argb(alpha: Float, red: Float, green: Float, blue: Float) =
+ ((alpha * 255.0f + 0.5f).toInt() shl 24) or
+ ((red * 255.0f + 0.5f).toInt() shl 16) or
+ ((green * 255.0f + 0.5f).toInt() shl 8) or
+ (blue * 255.0f + 0.5f).toInt()
diff --git a/graphics/graphics-path/src/main/androidx/graphics/androidx-graphics-graphics-path-documentation.md b/graphics/graphics-path/src/main/androidx/graphics/androidx-graphics-graphics-path-documentation.md
new file mode 100644
index 0000000..6c44750
--- /dev/null
+++ b/graphics/graphics-path/src/main/androidx/graphics/androidx-graphics-graphics-path-documentation.md
@@ -0,0 +1,117 @@
+# Package androidx.graphics.paths
+
+Androidx Graphics Path is an Android library that provides new functionalities around the
+[Path](https://developer.android.com/reference/android/graphics/Path) API. Specifically, it
+allows paths to be queried for the segment data they contain,
+
+The library is compatible with API 21+.
+
+## Iterating over a Path
+
+With Pathway you can easily iterate over a `Path` object to inspect its segments
+(curves or commands):
+
+```kotlin
+val path = Path().apply {
+ // Build path content
+}
+
+for (segment in path) {
+ val type = segment.type // The type of segment (move, cubic, quadratic, line, close, etc.)
+ val points = segment.points // The points describing the segment geometry
+}
+```
+
+This type of iteration is easy to use but may create an allocation per segment iterated over.
+If you must avoid allocations, Pathway provides a lower-level API to do so:
+
+```kotlin
+val path = Path().apply {
+ // Build path content
+}
+
+val iterator = path.iterator
+val points = FloatArray(8)
+
+while (iterator.hasNext()) {
+ val type = iterator.next(points) // The type of segment
+ // Read the segment geometry from the points array depending on the type
+}
+
+```
+
+### Path segments
+
+Each segment in a `Path` can be of one of the following types:
+
+#### Move
+
+Move command. The path segment contains 1 point indicating the move destination.
+The weight is set 0.0f and not meaningful.
+
+#### Line
+
+Line curve. The path segment contains 2 points indicating the two extremities of
+the line. The weight is set 0.0f and not meaningful.
+
+#### Quadratic
+
+Quadratic curve. The path segment contains 3 points in the following order:
+- Start point
+- Control point
+- End point
+
+The weight is set 0.0f and not meaningful.
+
+#### Conic
+
+Conic curve. The path segment contains 3 points in the following order:
+- Start point
+- Control point
+- End point
+
+The curve is weighted by the `PathSegment.weight` property.
+
+Conic curves are automatically converted to quadratic curves by default, see
+[Handling conic segments](#handling-conic-segments) below for more information.
+
+#### Cubic
+
+Cubic curve. The path segment contains 4 points in the following order:
+- Start point
+- First control point
+- Second control point
+- End point
+
+The weight is set to 0.0f and is not meaningful.
+
+#### Close
+
+Close command. Close the current contour by joining the last point added to the
+path with the first point of the current contour. The segment does not contain
+any point. The weight is set 0.0f and not meaningful.
+
+#### Done
+
+Done command. This optional command indicates that no further segment will be
+found in the path. It typically indicates the end of an iteration over a path
+and can be ignored.
+
+## Handling conic segments
+
+In some API levels, paths may contain conic curves (weighted quadratics) but the
+`Path` API does not offer a way to add conics to a `Path` object. To work around
+this, Pathway automatically converts conics into several quadratics by default.
+
+The conic to quadratic conversion is an approximation controlled by a tolerance
+threshold, set by default to 0.25f (sub-pixel). If you want to preserve conics
+or control the tolerance, you can use the following APIs:
+
+```kotlin
+// Preserve conics
+val iterator = path.iterator(PathIterator.ConicEvaluation.AsConic)
+
+// Control the tolerance of the conic to quadratic conversion
+val iterator = path.iterator(PathIterator.ConicEvaluation.AsQuadratics, 2.0f)
+
+```
diff --git a/graphics/graphics-path/src/main/cpp/CMakeLists.txt b/graphics/graphics-path/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..e77704f
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.18.1)
+project("androidx.graphics.path")
+
+add_library(
+ androidx.graphics.path
+ SHARED
+ Conic.cpp
+ PathIterator.cpp
+ pathway.cpp
+)
+
+find_library(
+ log-lib
+ log
+)
+
+target_link_libraries(
+ androidx.graphics.path
+ ${log-lib}
+)
diff --git a/graphics/graphics-path/src/main/cpp/Conic.cpp b/graphics/graphics-path/src/main/cpp/Conic.cpp
new file mode 100644
index 0000000..a6d15b6
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/Conic.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2006 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.
+ */
+
+#include "Conic.h"
+
+#include "scalar.h"
+
+#include "math/vec2.h"
+
+#include <cmath>
+#include <cstring>
+
+using namespace filament::math;
+
+constexpr int kMaxConicToQuadCount = 5;
+
+constexpr bool isFinite(const Point points[], int count) noexcept {
+ return isFinite(&points[0].x, count << 1);
+}
+
+constexpr bool isFinite(const Point& point) noexcept {
+ float a = 0.0f;
+ a *= point.x;
+ a *= point.y;
+ return a == 0.0f;
+}
+
+constexpr Point toPoint(const float2& v) noexcept {
+ return { .x = v.x, .y = v.y };
+}
+
+constexpr float2 fromPoint(const Point& v) noexcept {
+ return float2{v.x, v.y};
+}
+
+int conicToQuadratics(
+ const Point conicPoints[3], Point *quadraticPoints, int bufferSize,
+ float weight, float tolerance
+) noexcept {
+ Conic conic(conicPoints[0], conicPoints[1], conicPoints[2], weight);
+
+ int count = conic.computeQuadraticCount(tolerance);
+ int quadraticCount = 1 << count;
+ if (quadraticCount > bufferSize) {
+ // Buffer not large enough; return necessary size to resize and try again
+ return quadraticCount;
+ }
+ quadraticCount = conic.splitIntoQuadratics(quadraticPoints, count);
+
+ return quadraticCount;
+}
+
+int Conic::computeQuadraticCount(float tolerance) const noexcept {
+ if (tolerance <= 0.0f || !isFinite(tolerance) || !isFinite(points, 3)) return 0;
+
+ float a = weight - 1.0f;
+ float k = a / (4.0f * (2.0f + a));
+ float x = k * (points[0].x - 2.0f * points[1].x + points[2].x);
+ float y = k * (points[0].y - 2.0f * points[1].y + points[2].y);
+
+ float error = std::sqrtf(x * x + y * y);
+ int count = 0;
+ for ( ; count < kMaxConicToQuadCount; count++) {
+ if (error <= tolerance) break;
+ error *= 0.25f;
+ }
+
+ return count;
+}
+
+static Point* subdivide(const Conic& src, Point pts[], int level) {
+ if (level == 0) {
+ memcpy(pts, &src.points[1], 2 * sizeof(Point));
+ return pts + 2;
+ } else {
+ Conic dst[2];
+ src.split(dst);
+ const float startY = src.points[0].y;
+ const float endY = src.points[2].y;
+ if (between(startY, src.points[1].y, endY)) {
+ float midY = dst[0].points[2].y;
+ if (!between(startY, midY, endY)) {
+ float closerY = tabs(midY - startY) < tabs(midY - endY) ? startY : endY;
+ dst[0].points[2].y = dst[1].points[0].y = closerY;
+ }
+ if (!between(startY, dst[0].points[1].y, dst[0].points[2].y)) {
+ dst[0].points[1].y = startY;
+ }
+ if (!between(dst[1].points[0].y, dst[1].points[1].y, endY)) {
+ dst[1].points[1].y = endY;
+ }
+ }
+ --level;
+ pts = subdivide(dst[0], pts, level);
+ return subdivide(dst[1], pts, level);
+ }
+}
+
+void Conic::split(Conic* __restrict__ dst) const noexcept {
+ float2 scale{1.0f / (1.0f + weight)};
+ float newW = std::sqrtf(0.5f + weight * 0.5f);
+
+ float2 p0 = fromPoint(points[0]);
+ float2 p1 = fromPoint(points[1]);
+ float2 p2 = fromPoint(points[2]);
+ float2 ww(weight);
+
+ float2 wp1 = ww * p1;
+ float2 m = (p0 + (wp1 + wp1) + p2) * scale * float2(0.5f);
+ Point pt = toPoint(m);
+ if (!isFinite(pt)) {
+ double w_d = weight;
+ double w_2 = w_d * 2.0;
+ double scale_half = 1.0 / (1.0 + w_d) * 0.5;
+ pt.x = float((points[0].x + w_2 * points[1].x + points[2].x) * scale_half);
+ pt.y = float((points[0].y + w_2 * points[1].y + points[2].y) * scale_half);
+ }
+ dst[0].points[0] = points[0];
+ dst[0].points[1] = toPoint((p0 + wp1) * scale);
+ dst[0].points[2] = dst[1].points[0] = pt;
+ dst[1].points[1] = toPoint((wp1 + p2) * scale);
+ dst[1].points[2] = points[2];
+
+ dst[0].weight = dst[1].weight = newW;
+}
+
+int Conic::splitIntoQuadratics(Point dstPoints[], int count) const noexcept {
+ *dstPoints = points[0];
+
+ if (count >= kMaxConicToQuadCount) {
+ Conic dst[2];
+ split(dst);
+
+ if (equals(dst[0].points[1], dst[0].points[2]) &&
+ equals(dst[1].points[0], dst[1].points[1])) {
+ dstPoints[1] = dstPoints[2] = dstPoints[3] = dst[0].points[1];
+ dstPoints[4] = dst[1].points[2];
+ count = 1;
+ goto commonFinitePointCheck;
+ }
+ }
+
+ subdivide(*this, dstPoints + 1, count);
+
+commonFinitePointCheck:
+ const int quadCount = 1 << count;
+ const int pointCount = 2 * quadCount + 1;
+
+ if (!isFinite(dstPoints, pointCount)) {
+ for (int i = 1; i < pointCount - 1; ++i) {
+ dstPoints[i] = points[1];
+ }
+ }
+
+ return quadCount;
+}
\ No newline at end of file
diff --git a/graphics/graphics-path/src/main/cpp/Conic.h b/graphics/graphics-path/src/main/cpp/Conic.h
new file mode 100644
index 0000000..548fea2
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/Conic.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PATH_CONIC_H
+#define PATH_CONIC_H
+
+#include "Path.h"
+
+#include <vector>
+
+constexpr int kDefaultQuadraticCount = 8;
+
+int conicToQuadratics(
+ const Point conicPoints[3], Point *quadraticPoints, int bufferSize,
+ float weight, float tolerance
+) noexcept;
+
+class ConicConverter {
+public:
+ ConicConverter() noexcept { }
+
+private:
+ std::vector<Point> mStorage{1 + 2 * kDefaultQuadraticCount};
+};
+
+struct Conic {
+ Conic() noexcept { }
+
+ Conic(Point p0, Point p1, Point p2, float weight) noexcept {
+ points[0] = p0;
+ points[1] = p1;
+ points[2] = p2;
+ this->weight = weight;
+ }
+
+ void split(Conic* __restrict__ dst) const noexcept;
+ int computeQuadraticCount(float tolerance) const noexcept;
+ int splitIntoQuadratics(Point dstPoints[], int count) const noexcept;
+
+ Point points[3];
+ float weight;
+};
+
+#endif //PATH_CONIC_H
diff --git a/graphics/graphics-path/src/main/cpp/Path.h b/graphics/graphics-path/src/main/cpp/Path.h
new file mode 100644
index 0000000..f25d708
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/Path.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PATH_PATH_H
+#define PATH_PATH_H
+
+#include <stdint.h>
+
+// The following structures declare the minimum we need + a marker (generationId) to
+// validate the data during debugging. There may be more fields in the Skia structures
+// but we just ignore them for now. Some fields declared in older API levels (isFinite
+// for instance) may not show up in the declarations for newer API levels if the field
+// still exist but was moved after the data we need.
+
+enum class Verb : uint8_t {
+ Move,
+ Line,
+ Quadratic,
+ Conic,
+ Cubic,
+ Close,
+ Done
+};
+
+struct Point {
+ float x;
+ float y;
+};
+
+struct PathRef21 {
+ __unused intptr_t pointer; // Virtual tables
+ __unused int32_t refCount;
+ __unused float left;
+ __unused float top;
+ __unused float right;
+ __unused float bottom;
+ __unused uint8_t segmentMask; // Some of the unused fields are in a different order in 22/23
+ __unused uint8_t boundsIsDirty;
+ __unused uint8_t isFinite;
+ __unused uint8_t isOval;
+ Point* points;
+ Verb* verbs;
+ int verbCount;
+ __unused int pointCount;
+ __unused size_t freeSpace;
+ float* conicWeights;
+ __unused int conicWeightsReserve;
+ __unused int conicWeightsCount;
+ __unused uint32_t generationId;
+};
+
+struct PathRef24 {
+ __unused intptr_t pointer;
+ __unused int32_t refCount;
+ __unused float left;
+ __unused float top;
+ __unused float right;
+ __unused float bottom;
+ Point* points;
+ Verb* verbs;
+ int verbCount;
+ __unused int pointCount;
+ __unused size_t freeSpace;
+ float* conicWeights;
+ __unused int conicWeightsReserve;
+ __unused int conicWeightsCount;
+ __unused uint32_t generationId;
+};
+
+struct PathRef26 {
+ __unused int32_t refCount;
+ __unused float left;
+ __unused float top;
+ __unused float right;
+ __unused float bottom;
+ Point* points;
+ Verb* verbs;
+ int verbCount;
+ __unused int pointCount;
+ __unused size_t freeSpace;
+ float* conicWeights;
+ __unused int conicWeightsReserve;
+ __unused int conicWeightsCount;
+ __unused uint32_t generationId;
+};
+
+struct PathRef30 {
+ __unused int32_t refCount;
+ __unused float left;
+ __unused float top;
+ __unused float right;
+ __unused float bottom;
+ Point* points;
+ __unused int pointReserve;
+ __unused int pointCount;
+ Verb* verbs;
+ __unused int verbReserve;
+ int verbCount;
+ float* conicWeights;
+ __unused int conicWeightsReserve;
+ __unused int conicWeightsCount;
+ __unused uint32_t generationId;
+};
+
+struct Path {
+ PathRef21* pathRef;
+};
+
+#endif //PATH_PATH_H
diff --git a/graphics/graphics-path/src/main/cpp/PathIterator.cpp b/graphics/graphics-path/src/main/cpp/PathIterator.cpp
new file mode 100644
index 0000000..a77e251
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/PathIterator.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PathIterator.h"
+
+int PathIterator::count() noexcept {
+ int count = 0;
+ const Verb* verbs = mVerbs;
+ const Point* points = mPoints;
+ const float* conicWeights = mConicWeights;
+
+ for (int i = 0; i < mCount; i++) {
+ Verb verb = *(mDirection == VerbDirection::Forward ? verbs++ : --verbs);
+ switch (verb) {
+ case Verb::Move:
+ case Verb::Line:
+ points += 1;
+ count++;
+ break;
+ case Verb::Quadratic:
+ points += 2;
+ count++;
+ break;
+ case Verb::Conic:
+ points += 2;
+ count++;
+ break;
+ case Verb::Cubic:
+ points += 3;
+ count++;
+ break;
+ case Verb::Close:
+ case Verb::Done:
+ count++;
+ break;
+ }
+ }
+
+ return count;
+}
+
+Verb PathIterator::next(Point points[4]) noexcept {
+ if (mIndex <= 0) {
+ return Verb::Done;
+ }
+ mIndex--;
+
+ Verb verb = *(mDirection == VerbDirection::Forward ? mVerbs++ : --mVerbs);
+ switch (verb) {
+ case Verb::Move:
+ points[0] = mPoints[0];
+ mPoints += 1;
+ break;
+ case Verb::Line:
+ points[0] = mPoints[-1];
+ points[1] = mPoints[0];
+ mPoints += 1;
+ break;
+ case Verb::Quadratic:
+ points[0] = mPoints[-1];
+ points[1] = mPoints[0];
+ points[2] = mPoints[1];
+ mPoints += 2;
+ break;
+ case Verb::Conic:
+ points[0] = mPoints[-1];
+ points[1] = mPoints[0];
+ points[2] = mPoints[1];
+ points[3].x = *mConicWeights;
+ points[3].y = *mConicWeights;
+ mConicWeights++;
+ mPoints += 2;
+ break;
+ case Verb::Cubic:
+ points[0] = mPoints[-1];
+ points[1] = mPoints[0];
+ points[2] = mPoints[1];
+ points[3] = mPoints[2];
+ mPoints += 3;
+ break;
+ case Verb::Close:
+ case Verb::Done:
+ break;
+ }
+
+ return verb;
+}
diff --git a/graphics/graphics-path/src/main/cpp/PathIterator.h b/graphics/graphics-path/src/main/cpp/PathIterator.h
new file mode 100644
index 0000000..f814863
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/PathIterator.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PATH_PATH_ITERATOR_H
+#define PATH_PATH_ITERATOR_H
+
+#include "Path.h"
+#include "Conic.h"
+
+class PathIterator {
+public:
+ enum class VerbDirection : uint8_t {
+ Forward, // API >=30
+ Backward // API < 30
+ };
+
+ PathIterator(
+ Point* points,
+ Verb* verbs,
+ float* conicWeights,
+ int count,
+ VerbDirection direction
+ ) noexcept
+ : mPoints(points),
+ mVerbs(verbs),
+ mConicWeights(conicWeights),
+ mIndex(count),
+ mCount(count),
+ mDirection(direction) {
+ }
+
+ int rawCount() const noexcept { return mCount; }
+
+ int count() noexcept;
+
+ bool hasNext() const noexcept { return mIndex > 0; }
+
+ Verb peek() const noexcept {
+ auto verbs = mDirection == VerbDirection::Forward ? mVerbs : mVerbs - 1;
+ return mIndex > 0 ? *verbs : Verb::Done;
+ }
+
+ Verb next(Point points[4]) noexcept;
+
+private:
+ const Point* mPoints;
+ const Verb* mVerbs;
+ const float* mConicWeights;
+ int mIndex;
+ const int mCount;
+ const VerbDirection mDirection;
+ ConicConverter mConverter;
+};
+
+#endif //PATH_PATH_ITERATOR_H
diff --git a/graphics/graphics-path/src/main/cpp/math/TVecHelpers.h b/graphics/graphics-path/src/main/cpp/math/TVecHelpers.h
new file mode 100644
index 0000000..be00ebd
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/math/TVecHelpers.h
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef TNT_MATH_TVECHELPERS_H
+#define TNT_MATH_TVECHELPERS_H
+
+#include "compiler.h"
+
+#include <cmath> // for std:: namespace
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace filament {
+namespace math {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+template<typename U>
+inline constexpr U min(U a, U b) noexcept {
+ return a < b ? a : b;
+}
+
+template<typename U>
+inline constexpr U max(U a, U b) noexcept {
+ return a > b ? a : b;
+}
+
+template<typename T, typename U>
+struct arithmetic_result {
+ using type = decltype(std::declval<T>() + std::declval<U>());
+};
+
+template<typename T, typename U>
+using arithmetic_result_t = typename arithmetic_result<T, U>::type;
+
+template<typename A, typename B = int, typename C = int, typename D = int>
+using enable_if_arithmetic_t = std::enable_if_t<
+ is_arithmetic<A>::value &&
+ is_arithmetic<B>::value &&
+ is_arithmetic<C>::value &&
+ is_arithmetic<D>::value>;
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include math/vec{2|3|4}.h
+ */
+
+/*
+ * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template<template<typename T> class VECTOR, typename T>
+class TVecAddOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename U>
+ constexpr VECTOR<T>& operator+=(const VECTOR<U>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
+ }
+ return lhs;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ constexpr VECTOR<T>& operator+=(U v) {
+ return operator+=(VECTOR<U>(v));
+ }
+
+ template<typename U>
+ constexpr VECTOR<T>& operator-=(const VECTOR<U>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
+ }
+ return lhs;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ constexpr VECTOR<T>& operator-=(U v) {
+ return operator-=(VECTOR<U>(v));
+ }
+
+private:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, const VECTOR<U>& rv)
+ {
+ VECTOR<arithmetic_result_t<T, U>> res(lv);
+ res += rv;
+ return res;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(const VECTOR<T>& lv, U rv) {
+ return lv + VECTOR<U>(rv);
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator+(U lv, const VECTOR<T>& rv) {
+ return VECTOR<U>(lv) + rv;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, const VECTOR<U>& rv)
+ {
+ VECTOR<arithmetic_result_t<T, U>> res(lv);
+ res -= rv;
+ return res;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(const VECTOR<T>& lv, U rv) {
+ return lv - VECTOR<U>(rv);
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator-(U lv, const VECTOR<T>& rv) {
+ return VECTOR<U>(lv) - rv;
+ }
+};
+
+template<template<typename T> class VECTOR, typename T>
+class TVecProductOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename U>
+ constexpr VECTOR<T>& operator*=(const VECTOR<U>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
+ }
+ return lhs;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ constexpr VECTOR<T>& operator*=(U v) {
+ return operator*=(VECTOR<U>(v));
+ }
+
+ template<typename U>
+ constexpr VECTOR<T>& operator/=(const VECTOR<U>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
+ }
+ return lhs;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ constexpr VECTOR<T>& operator/=(U v) {
+ return operator/=(VECTOR<U>(v));
+ }
+
+private:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, const VECTOR<U>& rv)
+ {
+ VECTOR<arithmetic_result_t<T, U>> res(lv);
+ res *= rv;
+ return res;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(const VECTOR<T>& lv, U rv) {
+ return lv * VECTOR<U>(rv);
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator*(U lv, const VECTOR<T>& rv) {
+ return VECTOR<U>(lv) * rv;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, const VECTOR<U>& rv)
+ {
+ VECTOR<arithmetic_result_t<T, U>> res(lv);
+ res /= rv;
+ return res;
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(const VECTOR<T>& lv, U rv) {
+ return lv / VECTOR<U>(rv);
+ }
+
+ template<typename U, typename = enable_if_arithmetic_t<U>>
+ friend inline constexpr
+ VECTOR<arithmetic_result_t<T, U>> MATH_PURE operator/(U lv, const VECTOR<T>& rv) {
+ return VECTOR<U>(lv) / rv;
+ }
+};
+
+/*
+ * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ *
+ * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecUnaryOperators {
+public:
+ constexpr VECTOR<T> operator-() const {
+ VECTOR<T> r{};
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ for (size_t i = 0; i < r.size(); i++) {
+ r[i] = -rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecComparisonOperators implements relational/comparison operators
+ * on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecComparisonOperators {
+private:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename U>
+ friend inline constexpr
+ bool MATH_PURE operator==(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ // w/ inlining we end-up with many branches that will pollute the BPU cache
+ MATH_NOUNROLL
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] != rv[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ bool MATH_PURE operator!=(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ return !operator==(lv, rv);
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<bool> MATH_PURE equal(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] == rv[i];
+ }
+ return r;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<bool> MATH_PURE notEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] != rv[i];
+ }
+ return r;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<bool> MATH_PURE lessThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] < rv[i];
+ }
+ return r;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<bool> MATH_PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] <= rv[i];
+ }
+ return r;
+ }
+
+ template<typename U>
+ friend inline constexpr
+ VECTOR<bool> MATH_PURE greaterThan(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] > rv[i];
+ }
+ return r;
+ }
+
+ template<typename U>
+ friend inline
+ VECTOR<bool> MATH_PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ VECTOR<bool> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] >= rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecFunctions implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecFunctions {
+private:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename U>
+ friend constexpr inline
+ arithmetic_result_t<T, U> MATH_PURE dot(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ arithmetic_result_t<T, U> r{};
+ for (size_t i = 0; i < lv.size(); i++) {
+ r += lv[i] * rv[i];
+ }
+ return r;
+ }
+
+ friend inline T MATH_PURE norm(const VECTOR<T>& lv) {
+ return std::sqrt(dot(lv, lv));
+ }
+
+ friend inline T MATH_PURE length(const VECTOR<T>& lv) {
+ return norm(lv);
+ }
+
+ friend inline constexpr T MATH_PURE norm2(const VECTOR<T>& lv) {
+ return dot(lv, lv);
+ }
+
+ friend inline constexpr T MATH_PURE length2(const VECTOR<T>& lv) {
+ return norm2(lv);
+ }
+
+ template<typename U>
+ friend inline constexpr
+ arithmetic_result_t<T, U> MATH_PURE distance(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ return length(rv - lv);
+ }
+
+ template<typename U>
+ friend inline constexpr
+ arithmetic_result_t<T, U> MATH_PURE distance2(const VECTOR<T>& lv, const VECTOR<U>& rv) {
+ return length2(rv - lv);
+ }
+
+ friend inline VECTOR<T> MATH_PURE normalize(const VECTOR<T>& lv) {
+ return lv * (T(1) / length(lv));
+ }
+
+ friend inline VECTOR<T> MATH_PURE rcp(VECTOR<T> v) {
+ return T(1) / v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE abs(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = v[i] < 0 ? -v[i] : v[i];
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE floor(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::floor(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE ceil(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::ceil(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE round(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::round(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE inversesqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = T(1) / std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE sqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE cbrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::cbrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE exp(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::exp(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, T p) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::pow(v[i], p);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE pow(T v, VECTOR<T> p) {
+ for (size_t i = 0; i < p.size(); i++) {
+ p[i] = std::pow(v, p[i]);
+ }
+ return p;
+ }
+
+ friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, VECTOR<T> p) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::pow(v[i], p[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE log(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::log(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE log10(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::log10(v[i]);
+ }
+ return v;
+ }
+
+ friend inline VECTOR<T> MATH_PURE log2(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::log2(v[i]);
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE saturate(const VECTOR<T>& lv) {
+ return clamp(lv, T(0), T(1));
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, T min, T max) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = details::min(max, details::max(min, v[i]));
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE clamp(VECTOR<T> v, VECTOR<T> min, VECTOR<T> max) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = details::min(max[i], details::max(min[i], v[i]));
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv,
+ VECTOR<T> a) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ a[i] += (lv[i] * rv[i]);
+ }
+ return a;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = details::min(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = details::max(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline constexpr T MATH_PURE max(const VECTOR<T>& v) {
+ T r(v[0]);
+ for (size_t i = 1; i < v.size(); i++) {
+ r = max(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline constexpr T MATH_PURE min(const VECTOR<T>& v) {
+ T r(v[0]);
+ for (size_t i = 1; i < v.size(); i++) {
+ r = min(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE mix(const VECTOR<T>& u, VECTOR<T> v, T a) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = u[i] * (T(1) - a) + v[i] * a;
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE smoothstep(T edge0, T edge1, VECTOR<T> v) {
+ VECTOR<T> t = saturate((v - edge0) / (edge1 - edge0));
+ return t * t * (T(3) - T(2) * t);
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE step(T edge, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = v[i] < edge ? T(0) : T(1);
+ }
+ return v;
+ }
+
+ friend inline constexpr VECTOR<T> MATH_PURE step(VECTOR<T> edge, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = v[i] < edge[i] ? T(0) : T(1);
+ }
+ return v;
+ }
+
+ friend inline constexpr bool MATH_PURE any(const VECTOR<T>& v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i] != T(0)) return true;
+ }
+ return false;
+ }
+
+ friend inline constexpr bool MATH_PURE all(const VECTOR<T>& v) {
+ bool result = true;
+ for (size_t i = 0; i < v.size(); i++) {
+ result &= (v[i] != T(0));
+ }
+ return result;
+ }
+};
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace math
+} // namespace filament
+
+#endif // TNT_MATH_TVECHELPERS_H
diff --git a/graphics/graphics-path/src/main/cpp/math/compiler.h b/graphics/graphics-path/src/main/cpp/math/compiler.h
new file mode 100644
index 0000000..d6e18aa
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/math/compiler.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PATH_MATH_COMPILER_H
+#define PATH_MATH_COMPILER_H
+
+#include <type_traits>
+
+#if defined (WIN32)
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef far
+#undef far
+#endif
+
+#ifdef near
+#undef near
+#endif
+
+#endif
+
+// compatibility with non-clang compilers...
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__builtin_expect)
+# ifdef __cplusplus
+# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+# else
+# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+# endif
+#else
+# define MATH_LIKELY( exp ) (exp)
+# define MATH_UNLIKELY( exp ) (exp)
+#endif
+
+#if __has_attribute(unused)
+# define MATH_UNUSED __attribute__((unused))
+#else
+# define MATH_UNUSED
+#endif
+
+#if __has_attribute(pure)
+# define MATH_PURE __attribute__((pure))
+#else
+# define MATH_PURE
+#endif
+
+#ifdef _MSC_VER
+# define MATH_EMPTY_BASES __declspec(empty_bases)
+
+// MSVC does not support loop unrolling hints
+# define MATH_NOUNROLL
+
+// Sadly, MSVC does not support __builtin_constant_p
+# ifndef MAKE_CONSTEXPR
+# define MAKE_CONSTEXPR(e) (e)
+# endif
+
+// About value initialization, the C++ standard says:
+// if T is a class type with a default constructor that is neither user-provided nor deleted
+// (that is, it may be a class with an implicitly-defined or defaulted default constructor),
+// the object is zero-initialized and then it is default-initialized
+// if it has a non-trivial default constructor;
+// Unfortunately, MSVC always calls the default constructor, even if it is trivial, which
+// breaks constexpr-ness. To workaround this, we're always zero-initializing TVecN<>
+# define MATH_CONSTEXPR_INIT {}
+# define MATH_DEFAULT_CTOR {}
+# define MATH_DEFAULT_CTOR_CONSTEXPR constexpr
+# define CONSTEXPR_IF_NOT_MSVC // when declared constexpr, msvc fails with
+ // "failure was caused by cast of object of dynamic type"
+
+#else // _MSC_VER
+
+# define MATH_EMPTY_BASES
+// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax.
+# define MATH_NOUNROLL _Pragma("nounroll")
+
+# ifndef MAKE_CONSTEXPR
+# define MAKE_CONSTEXPR(e) __builtin_constant_p(e) ? (e) : (e)
+# endif
+
+# define MATH_CONSTEXPR_INIT
+# define MATH_DEFAULT_CTOR = default;
+# define MATH_DEFAULT_CTOR_CONSTEXPR
+# define CONSTEXPR_IF_NOT_MSVC constexpr
+
+#endif // _MSC_VER
+
+namespace filament::math {
+
+// MSVC 2019 16.4 doesn't seem to like it when we specialize std::is_arithmetic for
+// filament::math::half, so we're forced to create our own is_arithmetic here and specialize it
+// inside of half.h.
+template<typename T>
+struct is_arithmetic : std::integral_constant<bool,
+ std::is_integral<T>::value || std::is_floating_point<T>::value> {
+};
+
+} // filament::math
+
+#endif // PATH_MATH_COMPILER_H
diff --git a/graphics/graphics-path/src/main/cpp/math/vec2.h b/graphics/graphics-path/src/main/cpp/math/vec2.h
new file mode 100644
index 0000000..3228b09
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/math/vec2.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef TNT_MATH_VEC2_H
+#define TNT_MATH_VEC2_H
+
+#include "TVecHelpers.h"
+
+#include <type_traits>
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace filament {
+namespace math {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template<typename T>
+class MATH_EMPTY_BASES TVec2 :
+ public TVecProductOperators<TVec2, T>,
+ public TVecAddOperators<TVec2, T>,
+ public TVecUnaryOperators<TVec2, T>,
+ public TVecComparisonOperators<TVec2, T>,
+ public TVecFunctions<TVec2, T> {
+public:
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ static constexpr size_t SIZE = 2;
+
+ union {
+ T v[SIZE] MATH_CONSTEXPR_INIT;
+ struct { T x, y; };
+ struct { T s, t; };
+ struct { T r, g; };
+ };
+
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const noexcept {
+ assert(i < SIZE);
+ return v[i];
+ }
+
+ inline constexpr T& operator[](size_t i) noexcept {
+ assert(i < SIZE);
+ return v[i];
+ }
+
+ // constructors
+
+ // default constructor
+ MATH_DEFAULT_CTOR_CONSTEXPR TVec2() MATH_DEFAULT_CTOR
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = enable_if_arithmetic_t<A>>
+ constexpr TVec2(A v) noexcept : v{ T(v), T(v) } {}
+
+ template<typename A, typename B, typename = enable_if_arithmetic_t<A, B>>
+ constexpr TVec2(A x, B y) noexcept : v{ T(x), T(y) } {}
+
+ template<typename A, typename = enable_if_arithmetic_t<A>>
+ constexpr TVec2(const TVec2<A>& v) noexcept : v{ T(v[0]), T(v[1]) } {}
+
+ // cross product works only on vectors of size 2 or 3
+ template<typename U>
+ friend inline constexpr
+ arithmetic_result_t<T, U> cross(const TVec2& u, const TVec2<U>& v) noexcept {
+ return u[0] * v[1] - u[1] * v[0];
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+template<typename T, typename = details::enable_if_arithmetic_t<T>>
+using vec2 = details::TVec2<T>;
+
+using double2 = vec2<double>;
+using float2 = vec2<float>;
+using int2 = vec2<int32_t>;
+using uint2 = vec2<uint32_t>;
+using short2 = vec2<int16_t>;
+using ushort2 = vec2<uint16_t>;
+using byte2 = vec2<int8_t>;
+using ubyte2 = vec2<uint8_t>;
+using bool2 = vec2<bool>;
+
+// ----------------------------------------------------------------------------------------
+} // namespace math
+} // namespace filament
+
+#endif // TNT_MATH_VEC2_H
diff --git a/graphics/graphics-path/src/main/cpp/pathway.cpp b/graphics/graphics-path/src/main/cpp/pathway.cpp
new file mode 100644
index 0000000..9997387
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/pathway.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PathIterator.h"
+
+#include <jni.h>
+
+#include <sys/system_properties.h>
+
+#include <mutex>
+
+#define JNI_CLASS_NAME "androidx/graphics/path/PathIteratorPreApi34Impl"
+#define JNI_CLASS_NAME_CONVERTER "androidx/graphics/path/ConicConverter"
+
+#if !defined(NDEBUG)
+#include <android/log.h>
+#define ANDROID_LOG_TAG "PathIterator"
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, ANDROID_LOG_TAG, __VA_ARGS__)
+#endif
+
+struct {
+ jclass jniClass;
+ jfieldID nativePath;
+} sPath{};
+
+uint32_t sApiLevel = 0;
+std::once_flag sApiLevelOnceFlag;
+
+static uint32_t api_level() {
+ std::call_once(sApiLevelOnceFlag, []() {
+ char sdkVersion[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.sdk", sdkVersion);
+ sApiLevel = atoi(sdkVersion); // NOLINT(cert-err34-c)
+ });
+ return sApiLevel;
+}
+
+static jlong createPathIterator(JNIEnv* env, jobject,
+ jobject path_, jint conicEvaluation_, jfloat tolerance_) {
+
+ auto nativePath = static_cast<intptr_t>(env->GetLongField(path_, sPath.nativePath));
+ auto* path = reinterpret_cast<Path*>(nativePath);
+
+ Point* points;
+ Verb* verbs;
+ float* conicWeights;
+ int count;
+ PathIterator::VerbDirection direction;
+
+ const uint32_t apiLevel = api_level();
+ if (apiLevel >= 30) {
+ auto* ref = reinterpret_cast<PathRef30*>(path->pathRef);
+ points = ref->points;
+ verbs = ref->verbs;
+ conicWeights = ref->conicWeights;
+ count = ref->verbCount;
+ direction = PathIterator::VerbDirection::Forward;
+ } else if (apiLevel >= 26) {
+ auto* ref = reinterpret_cast<PathRef26*>(path->pathRef);
+ points = ref->points;
+ verbs = ref->verbs;
+ conicWeights = ref->conicWeights;
+ count = ref->verbCount;
+ direction = PathIterator::VerbDirection::Backward;
+ } else if (apiLevel >= 24) {
+ auto* ref = reinterpret_cast<PathRef24*>(path->pathRef);
+ points = ref->points;
+ verbs = ref->verbs;
+ conicWeights = ref->conicWeights;
+ count = ref->verbCount;
+ direction = PathIterator::VerbDirection::Backward;
+ } else {
+ auto* ref = path->pathRef;
+ points = ref->points;
+ verbs = ref->verbs;
+ conicWeights = ref->conicWeights;
+ count = ref->verbCount;
+ direction = PathIterator::VerbDirection::Backward;
+ }
+
+ return jlong(new PathIterator(points, verbs, conicWeights, count, direction));
+}
+
+static void destroyPathIterator(JNIEnv*, jobject, jlong pathIterator_) {
+ delete reinterpret_cast<PathIterator*>(pathIterator_);
+}
+
+static jboolean pathIteratorHasNext(JNIEnv*, jobject, jlong pathIterator_) {
+ return reinterpret_cast<PathIterator*>(pathIterator_)->hasNext();
+}
+
+static jint conicToQuadraticsWrapper(JNIEnv* env, jobject,
+ jfloatArray conicPoints, jfloatArray quadraticPoints,
+ jfloat weight, jfloat tolerance, jint offset) {
+ float *conicData1 = env->GetFloatArrayElements(conicPoints, JNI_FALSE);
+ float *quadData1 = env->GetFloatArrayElements(quadraticPoints, JNI_FALSE);
+ int quadDataSize = env->GetArrayLength(quadraticPoints);
+
+ int count = conicToQuadratics(reinterpret_cast<Point *>(conicData1 + offset),
+ reinterpret_cast<Point *>(quadData1),
+ env->GetArrayLength(quadraticPoints),
+ weight, tolerance);
+
+ env->ReleaseFloatArrayElements(conicPoints, conicData1, 0);
+ env->ReleaseFloatArrayElements(quadraticPoints, quadData1, 0);
+
+ return count;
+}
+
+static jint pathIteratorNext(JNIEnv* env, jobject,
+ jlong pathIterator_, jfloatArray points_, jint offset_) {
+ auto pathIterator = reinterpret_cast<PathIterator*>(pathIterator_);
+ Point pointsData[4];
+ Verb verb = pathIterator->next(pointsData);
+
+ if (verb != Verb::Done && verb != Verb::Close) {
+ auto* floatsData = reinterpret_cast<jfloat*>(pointsData);
+ env->SetFloatArrayRegion(points_, offset_, 8, floatsData);
+ }
+
+ return static_cast<jint>(verb);
+}
+
+static jint pathIteratorPeek(JNIEnv*, jobject, jlong pathIterator_) {
+ return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->peek());
+}
+
+static jint pathIteratorRawSize(JNIEnv*, jobject, jlong pathIterator_) {
+ return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->rawCount());
+}
+
+static jint pathIteratorSize(JNIEnv*, jobject, jlong pathIterator_) {
+ return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->count());
+}
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return -1;
+ }
+
+ sPath.jniClass = env->FindClass("android/graphics/Path");
+ if (sPath.jniClass == nullptr) return JNI_ERR;
+
+ sPath.nativePath = env->GetFieldID(sPath.jniClass, "mNativePath", "J");
+ if (sPath.nativePath == nullptr) return JNI_ERR;
+
+ {
+ jclass pathsClass = env->FindClass(JNI_CLASS_NAME);
+ if (pathsClass == nullptr) return JNI_ERR;
+
+ static const JNINativeMethod methods[] = {
+ {
+ (char*) "createInternalPathIterator",
+ (char*) "(Landroid/graphics/Path;IF)J",
+ reinterpret_cast<void*>(createPathIterator)
+ },
+ {
+ (char*) "destroyInternalPathIterator",
+ (char*) "(J)V",
+ reinterpret_cast<void*>(destroyPathIterator)
+ },
+ {
+ (char*) "internalPathIteratorHasNext",
+ (char*) "(J)Z",
+ reinterpret_cast<void*>(pathIteratorHasNext)
+ },
+ {
+ (char*) "internalPathIteratorNext",
+ (char*) "(J[FI)I",
+ reinterpret_cast<void*>(pathIteratorNext)
+ },
+ {
+ (char*) "internalPathIteratorPeek",
+ (char*) "(J)I",
+ reinterpret_cast<void*>(pathIteratorPeek)
+ },
+ {
+ (char*) "internalPathIteratorRawSize",
+ (char*) "(J)I",
+ reinterpret_cast<void*>(pathIteratorRawSize)
+ },
+ {
+ (char*) "internalPathIteratorSize",
+ (char*) "(J)I",
+ reinterpret_cast<void*>(pathIteratorSize)
+ },
+ };
+
+ int result = env->RegisterNatives(
+ pathsClass, methods, sizeof(methods) / sizeof(JNINativeMethod)
+ );
+ if (result != JNI_OK) return result;
+
+ env->DeleteLocalRef(pathsClass);
+
+ jclass converterClass = env->FindClass(JNI_CLASS_NAME_CONVERTER);
+ if (converterClass == nullptr) return JNI_ERR;
+ static const JNINativeMethod methods2[] = {
+ {
+ (char *) "internalConicToQuadratics",
+ (char *) "([F[FFFI)I",
+ reinterpret_cast<void *>(conicToQuadraticsWrapper)
+ },
+ };
+
+ result = env->RegisterNatives(
+ converterClass, methods2, sizeof(methods2) / sizeof(JNINativeMethod)
+ );
+ if (result != JNI_OK) return result;
+
+ env->DeleteLocalRef(converterClass);
+ }
+
+ return JNI_VERSION_1_6;
+}
diff --git a/graphics/graphics-path/src/main/cpp/scalar.h b/graphics/graphics-path/src/main/cpp/scalar.h
new file mode 100644
index 0000000..0342d13
--- /dev/null
+++ b/graphics/graphics-path/src/main/cpp/scalar.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PATH_SCALAR_H
+#define PATH_SCALAR_H
+
+union floatIntUnion {
+ float value;
+ int32_t signBitInt;
+};
+
+static inline int32_t float2Bits(float x) noexcept {
+ floatIntUnion data; // NOLINT(cppcoreguidelines-pro-type-member-init)
+ data.value = x;
+ return data.signBitInt;
+}
+
+constexpr bool isFloatFinite(int32_t bits) noexcept {
+ constexpr int32_t kFloatBitsExponentMask = 0x7F800000;
+ return (bits & kFloatBitsExponentMask) != kFloatBitsExponentMask;
+}
+
+static inline bool isFinite(float v) noexcept {
+ return isFloatFinite(float2Bits(v));
+}
+
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
+static bool canNormalize(float dx, float dy) noexcept {
+ return (isFinite(dx) && isFinite(dy)) && (dx || dy);
+}
+#pragma clang diagnostic pop
+
+static bool equals(const Point& p1, const Point& p2) noexcept {
+ return !canNormalize(p1.x - p2.x, p1.y - p2.y);
+}
+
+constexpr bool isFinite(const float array[], int count) noexcept {
+ float prod = 0.0f;
+ for (int i = 0; i < count; i++) {
+ prod *= array[i];
+ }
+ return prod == 0.0f;
+}
+
+template<typename T>
+constexpr T tabs(T value) noexcept {
+ if (value < 0) {
+ value = -value;
+ }
+ return value;
+}
+
+constexpr bool between(float a, float b, float c) noexcept {
+ return (a - b) * (c - b) <= 0.0f;
+}
+
+#endif //PATH_SCALAR_H
diff --git a/graphics/graphics-path/src/main/java/androidx/graphics/path/ConicConverter.kt b/graphics/graphics-path/src/main/java/androidx/graphics/path/ConicConverter.kt
new file mode 100644
index 0000000..445bd47
--- /dev/null
+++ b/graphics/graphics-path/src/main/java/androidx/graphics/path/ConicConverter.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.path
+
+import android.util.Log
+
+/**
+ * This class converts a given Conic object to the equivalent set of Quadratic objects.
+ * It stores all quadratics from a conversion in the call to [convert], but returns only
+ * one at a time, from nextQuadratic(), storing the rest for later retrieval (since a
+ * PathIterator only retrieves one object at a time).
+ *
+ * This object is stateful, using quadraticCount, currentQuadratic, and quadraticData
+ * to send back the next quadratic when requested, in [nextQuadratic].
+ */
+internal class ConicConverter() {
+
+ private val LOG_TAG = "ConicConverter"
+ private val DEBUG = false
+
+ /**
+ * The total number of quadratics currently stored in the converter
+ */
+ var quadraticCount: Int = 0
+ private set
+
+ /**
+ * The index of the current Quadratic; this is the next quadratic to be returned
+ * in the call to nextQuadratic().
+ */
+ var currentQuadratic = 0
+
+ /**
+ * Storage for all quadratics for a particular conic. Set to reasonable
+ * default size, will need to resize if we ever get a return count larger
+ * than the current size.
+ * Initial size holds up to 5 quadratics: 2 floats/point, 3 points/quadratic
+ * where all quadratics overlap in one point except the ends.
+ */
+ private var quadraticData = FloatArray(1)
+
+ /**
+ * This function stores the next converted quadratic in the given points array,
+ * returning true if this happened, false if there was no quadratic to be returned.
+ */
+ fun nextQuadratic(points: FloatArray, offset: Int = 0): Boolean {
+ if (currentQuadratic < quadraticCount) {
+ val index = currentQuadratic * 2 * 2
+ points[0 + offset] = quadraticData[index]
+ points[1 + offset] = quadraticData[index + 1]
+ points[2 + offset] = quadraticData[index + 2]
+ points[3 + offset] = quadraticData[index + 3]
+ points[4 + offset] = quadraticData[index + 4]
+ points[5 + offset] = quadraticData[index + 5]
+ currentQuadratic++
+ return true
+ }
+ return false
+ }
+
+ /**
+ * Converts the conic in [points] to a series of quadratics, which will all be stored
+ */
+ fun convert(points: FloatArray, weight: Float, tolerance: Float, offset: Int = 0) {
+ quadraticCount = internalConicToQuadratics(points, quadraticData, weight, tolerance, offset)
+ if (quadraticCount > quadraticData.size) {
+ if (DEBUG) Log.d(LOG_TAG, "Resizing quadraticData buffer to $quadraticCount")
+ quadraticData = FloatArray(quadraticCount * 4 * 2)
+ quadraticCount = internalConicToQuadratics(points, quadraticData, weight, tolerance,
+ offset)
+ }
+ currentQuadratic = 0
+ if (DEBUG) Log.d("ConicConverter", "internalConicToQuadratics returned " + quadraticCount)
+ }
+
+ /**
+ * The actual conversion from conic to quadratic data happens in native code, in the library
+ * loaded elsewhere. This JNI function wraps that native functionality.
+ */
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalConicToQuadratics(
+ conicPoints: FloatArray,
+ quadraticPoints: FloatArray,
+ weight: Float,
+ tolerance: Float,
+ offset: Int
+ ): Int
+}
\ No newline at end of file
diff --git a/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIterator.kt b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIterator.kt
new file mode 100644
index 0000000..6567419
--- /dev/null
+++ b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIterator.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("PathUtilities")
+package androidx.graphics.path
+
+import android.graphics.Path
+import androidx.core.os.BuildCompat
+import androidx.core.os.BuildCompat.PrereleaseSdkCheck
+
+/**
+ * A path iterator can be used to iterate over all the [segments][PathSegment] that make up
+ * a path. Those segments may in turn define multiple contours inside the path. Conic segments
+ * are by default evaluated as approximated quadratic segments. To preserve conic segments as
+ * conics, set [conicEvaluation] to [AsConic][ConicEvaluation.AsConic]. The error of the
+ * approximation is controlled by [tolerance].
+ *
+ * [PathIterator] objects are created implicitly through a given [Path] object; to create a
+ * [PathIterator], call one of the two [Path.iterator] extension functions.
+ */
+@Suppress("NotCloseable", "IllegalExperimentalApiUsage")
+@PrereleaseSdkCheck
+class PathIterator constructor(
+ val path: Path,
+ val conicEvaluation: ConicEvaluation = ConicEvaluation.AsQuadratics,
+ val tolerance: Float = 0.25f
+) : Iterator<PathSegment> {
+
+ internal val implementation: PathIteratorImpl
+ init {
+ implementation =
+ when {
+ // TODO: replace isAtLeastU() check with below or similar when U is released
+ // Build.VERSION.SDK_INT >= 34 -> {
+ BuildCompat.isAtLeastU() -> {
+ PathIteratorApi34Impl(path, conicEvaluation, tolerance)
+ }
+ else -> {
+ PathIteratorPreApi34Impl(path, conicEvaluation, tolerance)
+ }
+ }
+ }
+
+ enum class ConicEvaluation {
+ /**
+ * Conic segments are returned as conic segments.
+ */
+ AsConic,
+
+ /**
+ * Conic segments are returned as quadratic approximations. The quality of the
+ * approximation is defined by a tolerance value.
+ */
+ AsQuadratics
+ }
+
+ /**
+ * Returns the number of verbs present in this iterator, i.e. the number of calls to
+ * [next] required to complete the iteration.
+ *
+ * By default, [calculateSize] returns the true number of operations in the iterator. Deriving
+ * this result requires converting any conics to quadratics, if [conicEvaluation] is
+ * set to [ConicEvaluation.AsQuadratics], which takes extra processing time. Set
+ * [includeConvertedConics] to false if an approximate size, not including conic
+ * conversion, is sufficient.
+ *
+ * @param includeConvertedConics The returned size includes any required conic conversions.
+ * Default is true, so it will return the exact size, at the cost of iterating through
+ * all elements and converting any conics as appropriate. Set to false to save on processing,
+ * at the cost of a less exact result.
+ */
+ fun calculateSize(includeConvertedConics: Boolean = true) =
+ implementation.calculateSize(includeConvertedConics)
+
+ /**
+ * Returns `true` if the iteration has more elements.
+ */
+ override fun hasNext(): Boolean = implementation.hasNext()
+
+ /**
+ * Returns the type of the current segment in the iteration, or [Done][PathSegment.Type.Done]
+ * if the iteration is finished.
+ */
+ fun peek() = implementation.peek()
+
+ /**
+ * Returns the [type][PathSegment.Type] of the next [path segment][PathSegment] in the iteration
+ * and fills [points] with the points specific to the segment type. Each pair of floats in
+ * the [points] array represents a point for the given segment. The number of pairs of floats
+ * depends on the [PathSegment.Type]:
+ * - [Move][PathSegment.Type.Move]: 1 pair (indices 0 to 1)
+ * - [Move][PathSegment.Type.Line]: 2 pairs (indices 0 to 3)
+ * - [Move][PathSegment.Type.Quadratic]: 3 pairs (indices 0 to 5)
+ * - [Move][PathSegment.Type.Conic]: 4 pairs (indices 0 to 7), the last pair contains the
+ * [weight][PathSegment.weight] twice
+ * - [Move][PathSegment.Type.Cubic]: 4 pairs (indices 0 to 7)
+ * - [Close][PathSegment.Type.Close]: 0 pair
+ * - [Done][PathSegment.Type.Done]: 0 pair
+ * This method does not allocate any memory.
+ *
+ * @param points A [FloatArray] large enough to hold 8 floats starting at [offset],
+ * throws an [IllegalStateException] otherwise.
+ * @param offset Offset in [points] where to store the result
+ */
+ @JvmOverloads
+ fun next(points: FloatArray, offset: Int = 0): PathSegment.Type =
+ implementation.next(points, offset)
+
+ /**
+ * Returns the next [path segment][PathSegment] in the iteration, or [DoneSegment] if
+ * the iteration is finished. To save on allocations, use the alternative [next] function, which
+ * takes a [FloatArray].
+ */
+ override fun next(): PathSegment = implementation.next()
+}
+
+/**
+ * Creates a new [PathIterator] for this [path][android.graphics.Path] that evaluates
+ * conics as quadratics. To preserve conics, use the [Path.iterator] function that takes a
+ * [PathIterator.ConicEvaluation] parameter.
+ */
+@Suppress("IllegalExperimentalApiUsage")
+@PrereleaseSdkCheck
+operator fun Path.iterator() = PathIterator(this)
+
+/**
+ * Creates a new [PathIterator] for this [path][android.graphics.Path]. To preserve conics as
+ * conics (not convert them to quadratics), set [conicEvaluation] to
+ * [PathIterator.ConicEvaluation.AsConic].
+ */
+@Suppress("IllegalExperimentalApiUsage")
+@PrereleaseSdkCheck
+fun Path.iterator(conicEvaluation: PathIterator.ConicEvaluation, tolerance: Float = 0.25f) =
+ PathIterator(this, conicEvaluation, tolerance)
diff --git a/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIteratorImpl.kt b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIteratorImpl.kt
new file mode 100644
index 0000000..65fa2c1
--- /dev/null
+++ b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathIteratorImpl.kt
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.path
+
+import android.graphics.Path
+import android.graphics.PathIterator as PlatformPathIterator
+import android.graphics.PointF
+import androidx.annotation.RequiresApi
+import androidx.core.os.BuildCompat
+import androidx.graphics.path.PathIterator.ConicEvaluation
+
+/**
+ * Base class for API-version-specific PathIterator implementation classes. All functionality
+ * is implemented in the subclasses except for [next], which relies on shared native code
+ * to perform conic conversion.
+ */
+@Suppress("IllegalExperimentalApiUsage")
+@BuildCompat.PrereleaseSdkCheck
+internal abstract class PathIteratorImpl(
+ val path: Path,
+ val conicEvaluation: ConicEvaluation = ConicEvaluation.AsQuadratics,
+ val tolerance: Float = 0.25f
+) {
+ /**
+ * An iterator's ConicConverter converts from a conic to a series of
+ * quadratics. It keeps track of the resulting quadratics and iterates through
+ * them on ensuing calls to next(). The converter is only ever called if
+ * [conicEvaluation] is set to [ConicEvaluation.AsQuadratics].
+ */
+ var conicConverter = ConicConverter()
+
+ /**
+ * pointsData is used internally when the no-arg variant of next() is called,
+ * to avoid allocating a new array every time.
+ */
+ val pointsData = FloatArray(8)
+
+ private companion object {
+ init {
+ /**
+ * The native library is used mainly for pre-API34, but we also rely
+ * on the conic conversion code in API34+, thus it is initialized here.
+ */
+ System.loadLibrary("androidx.graphics.path")
+ }
+ }
+
+ abstract fun calculateSize(includeConvertedConics: Boolean): Int
+
+ abstract fun hasNext(): Boolean
+ abstract fun peek(): PathSegment.Type
+
+ /**
+ * The core functionality of [next] is in API-specific subclasses. But we implement [next]
+ * at this level to share the same conic conversion implementation across all versions.
+ * This happens by calling [nextImpl] to get the next segment from the subclasses, then
+ * calling the shared [ConicConverter] code when appropriate to get and return the
+ * converted segments.
+ */
+ abstract fun nextImpl(points: FloatArray, offset: Int = 0): PathSegment.Type
+
+ fun next(points: FloatArray, offset: Int = 0): PathSegment.Type {
+ check(points.size - offset >= 8) { "The points array must contain at least 8 floats" }
+ // First check to see if we are currently iterating through converted conics
+ if (conicConverter.currentQuadratic < conicConverter.quadraticCount
+ ) {
+ conicConverter.nextQuadratic(points, offset)
+ return (pathSegmentTypes[PathSegment.Type.Quadratic.ordinal])
+ } else {
+ val typeValue = nextImpl(points, offset)
+ if (typeValue == PathSegment.Type.Conic &&
+ conicEvaluation == ConicEvaluation.AsQuadratics
+ ) {
+ with(conicConverter) {
+ convert(points, points[6 + offset], tolerance, offset)
+ if (quadraticCount > 0) {
+ nextQuadratic(points, offset)
+ }
+ }
+ return PathSegment.Type.Quadratic
+ }
+ return typeValue
+ }
+ }
+
+ fun next(): PathSegment {
+ val type = next(pointsData, 0)
+ if (type == PathSegment.Type.Done) return DoneSegment
+ if (type == PathSegment.Type.Close) return CloseSegment
+ val weight = if (type == PathSegment.Type.Conic) pointsData[6] else 0.0f
+ return PathSegment(type, floatsToPoints(pointsData, type), weight)
+ }
+
+ /**
+ * Utility function to convert a FloatArray to an array of PointF objects, where
+ * every two Floats in the FloatArray correspond to a single PointF in the resulting
+ * point array. The FloatArray is used internally to process a next() call, the
+ * array of points is used to create a PathSegment from the operation.
+ */
+ private fun floatsToPoints(pointsData: FloatArray, type: PathSegment.Type): Array<PointF> {
+ val points = when (type) {
+ PathSegment.Type.Move -> {
+ arrayOf(PointF(pointsData[0], pointsData[1]))
+ }
+
+ PathSegment.Type.Line -> {
+ arrayOf(
+ PointF(pointsData[0], pointsData[1]),
+ PointF(pointsData[2], pointsData[3])
+ )
+ }
+
+ PathSegment.Type.Quadratic,
+ PathSegment.Type.Conic -> {
+ arrayOf(
+ PointF(pointsData[0], pointsData[1]),
+ PointF(pointsData[2], pointsData[3]),
+ PointF(pointsData[4], pointsData[5])
+ )
+ }
+
+ PathSegment.Type.Cubic -> {
+ arrayOf(
+ PointF(pointsData[0], pointsData[1]),
+ PointF(pointsData[2], pointsData[3]),
+ PointF(pointsData[4], pointsData[5]),
+ PointF(pointsData[6], pointsData[7])
+ )
+ }
+ // This should not happen because of the early returns above
+ else -> emptyArray()
+ }
+ return points
+ }
+}
+
+/**
+ * In API level 34, we can use new platform functionality for most of what PathIterator does.
+ * The exceptions are conic conversion (which is handled in the base impl class) and
+ * [calculateSize], which is implemented here.
+ */
+@RequiresApi(34)
+@Suppress("IllegalExperimentalApiUsage")
+@BuildCompat.PrereleaseSdkCheck
+internal class PathIteratorApi34Impl(
+ path: Path,
+ conicEvaluation: ConicEvaluation = ConicEvaluation.AsQuadratics,
+ tolerance: Float = 0.25f
+) : PathIteratorImpl(path, conicEvaluation, tolerance) {
+
+ /**
+ * The platform iterator handles most of what we need for iterating. We hold an instance
+ * of that object in this class.
+ */
+ private val platformIterator: PlatformPathIterator
+
+ init {
+ platformIterator = path.pathIterator
+ }
+
+ /**
+ * The platform does not expose a calculateSize() method, so we implement our own. In the
+ * simplest case, this is done by simply iterating through all segments until done. However, if
+ * the caller requested the true size (including any conic conversion) and if there are any
+ * conics in the path segments, then there is more work to do since we have to convert and count
+ * those segments as well.
+ */
+ override fun calculateSize(includeConvertedConics: Boolean): Int {
+ val convertConics = includeConvertedConics &&
+ conicEvaluation == ConicEvaluation.AsQuadratics
+ var numVerbs = 0
+ val tempIterator = path.pathIterator
+ val tempFloats = FloatArray(8)
+ while (tempIterator.hasNext()) {
+ val type = tempIterator.next(tempFloats, 0)
+ if (type == PlatformPathIterator.VERB_CONIC && convertConics) {
+ with(conicConverter) {
+ convert(tempFloats, tempFloats[6], tolerance)
+ numVerbs += quadraticCount
+ }
+ } else {
+ numVerbs++
+ }
+ }
+ return numVerbs
+ }
+
+ /**
+ * [nextImpl] is called by [next] in the base class to do the work of actually getting the
+ * next segment, for which we defer to the platform iterator.
+ */
+ override fun nextImpl(points: FloatArray, offset: Int): PathSegment.Type {
+ return platformToAndroidXSegmentType(platformIterator.next(points, offset))
+ }
+
+ override fun hasNext(): Boolean {
+ return platformIterator.hasNext()
+ }
+
+ override fun peek(): PathSegment.Type {
+ val platformType = platformIterator.peek()
+ return platformToAndroidXSegmentType(platformType)
+ }
+
+ /**
+ * Callers need the AndroidX segment types, so we must convert from the platform types.
+ */
+ private fun platformToAndroidXSegmentType(platformType: Int): PathSegment.Type {
+ return when (platformType) {
+ PlatformPathIterator.VERB_CLOSE -> PathSegment.Type.Close
+ PlatformPathIterator.VERB_CONIC -> PathSegment.Type.Conic
+ PlatformPathIterator.VERB_CUBIC -> PathSegment.Type.Cubic
+ PlatformPathIterator.VERB_DONE -> PathSegment.Type.Done
+ PlatformPathIterator.VERB_LINE -> PathSegment.Type.Line
+ PlatformPathIterator.VERB_MOVE -> PathSegment.Type.Move
+ PlatformPathIterator.VERB_QUAD -> PathSegment.Type.Quadratic
+ else -> {
+ throw IllegalArgumentException("Unknown path segment type $platformType")
+ }
+ }
+ }
+}
+
+/**
+ * Most of the functionality for pre-34 iteration is handled in the native code. The only
+ * exception, similar to the API34 implementation, is the calculateSize(). There is a size()
+ * function in native code which is very quick (it simply tracks the number of verbs in the native
+ * structure). But if the caller wants conic conversion, then we need to iterate through
+ * and convert appropriately, counting as we iterate.
+ */
+@Suppress("IllegalExperimentalApiUsage")
+@BuildCompat.PrereleaseSdkCheck
+internal class PathIteratorPreApi34Impl(
+ path: Path,
+ conicEvaluation: ConicEvaluation = ConicEvaluation.AsQuadratics,
+ tolerance: Float = 0.25f
+) : PathIteratorImpl(path, conicEvaluation, tolerance) {
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun createInternalPathIterator(
+ path: Path,
+ conicEvaluation: Int,
+ tolerance: Float
+ ): Long
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun destroyInternalPathIterator(internalPathIterator: Long)
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalPathIteratorHasNext(internalPathIterator: Long): Boolean
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalPathIteratorNext(
+ internalPathIterator: Long,
+ points: FloatArray,
+ offset: Int
+ ): Int
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalPathIteratorPeek(internalPathIterator: Long): Int
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalPathIteratorRawSize(internalPathIterator: Long): Int
+
+ @Suppress("KotlinJniMissingFunction")
+ private external fun internalPathIteratorSize(internalPathIterator: Long): Int
+ /**
+ * Defines the type of evaluation to apply to conic segments during iteration.
+ */
+
+ private val internalPathIterator =
+ createInternalPathIterator(path, ConicEvaluation.AsConic.ordinal, tolerance)
+
+ /**
+ * Returns the number of verbs present in this iterator's path. If [includeConvertedConics]
+ * property is false and the path has any conic elements, the returned size might be smaller
+ * than the number of calls to [next] required to fully iterate over the path. An accurate
+ * size can be computed by setting the parameter to true instead, at a performance cost.
+ * Including converted conics requires iterating through the entire path, including converting
+ * any conics along the way, to calculate the true size.
+ */
+ override fun calculateSize(includeConvertedConics: Boolean): Int {
+ var numVerbs = 0
+ if (!includeConvertedConics || conicEvaluation == ConicEvaluation.AsConic) {
+ numVerbs = internalPathIteratorSize(internalPathIterator)
+ } else {
+ val tempIterator =
+ createInternalPathIterator(path, ConicEvaluation.AsConic.ordinal, tolerance)
+ val tempFloats = FloatArray(8)
+ while (internalPathIteratorHasNext(tempIterator)) {
+ val segment = internalPathIteratorNext(tempIterator, tempFloats, 0)
+ when (pathSegmentTypes[segment]) {
+ PathSegment.Type.Conic -> {
+ conicConverter.convert(tempFloats, tempFloats[7], tolerance)
+ numVerbs += conicConverter.quadraticCount
+ }
+ else -> numVerbs++
+ }
+ }
+ }
+ return numVerbs
+ }
+
+ /**
+ * Returns `true` if the iteration has more elements.
+ */
+ override fun hasNext(): Boolean = internalPathIteratorHasNext(internalPathIterator)
+
+ /**
+ * Returns the type of the current segment in the iteration, or [Done][PathSegment.Type.Done]
+ * if the iteration is finished.
+ */
+ override fun peek() = pathSegmentTypes[internalPathIteratorPeek(internalPathIterator)]
+
+ /**
+ * This is where the actual work happens to get the next segment in the path, which happens
+ * in native code. This function is called by [next] in the base class, which then converts
+ * the resulting segment from conics to quadratics as necessary.
+ */
+ override fun nextImpl(points: FloatArray, offset: Int): PathSegment.Type {
+ return pathSegmentTypes[internalPathIteratorNext(internalPathIterator, points, offset)]
+ }
+
+ protected fun finalize() {
+ destroyInternalPathIterator(internalPathIterator)
+ }
+}
\ No newline at end of file
diff --git a/graphics/graphics-path/src/main/java/androidx/graphics/path/PathSegment.kt b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathSegment.kt
new file mode 100644
index 0000000..863d6d0
--- /dev/null
+++ b/graphics/graphics-path/src/main/java/androidx/graphics/path/PathSegment.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("PathSegmentUtilities")
+package androidx.graphics.path
+
+import android.graphics.PointF
+
+/**
+ * A path segment represents a curve (line, cubic, quadratic or conic) or a command inside
+ * a fully formed [path][android.graphics.Path] object.
+ *
+ * A segment is identified by a [type][PathSegment.Type] which in turns defines how many
+ * [points] are available (from 0 to 3) and whether the [weight] is meaningful. Please refer
+ * to the documentation of each [type][PathSegment.Type] for more information.
+ *
+ * A segment with the [Move][Type.Move] or [Close][Type.Close] is usually represented by
+ * the singletons [DoneSegment] and [CloseSegment] respectively.
+ *
+ * @property type The type that identifies this segment and defines the number of points.
+ * @property points An array of points describing this segment, whose size depends on [type].
+ * @property weight Conic weight, only valid if [type] is [Type.Conic].
+ */
+class PathSegment internal constructor(
+ val type: Type,
+ @get:Suppress("ArrayReturn") val points: Array<PointF>,
+ val weight: Float
+) {
+
+ /**
+ * Type of a given segment in a [path][android.graphics.Path], either a command
+ * ([Type.Move], [Type.Close], [Type.Done]) or a curve ([Type.Line], [Type.Cubic],
+ * [Type.Quadratic], [Type.Conic]).
+ */
+ enum class Type {
+ /**
+ * Move command, the path segment contains 1 point indicating the move destination.
+ * The weight is set 0.0f and not meaningful.
+ */
+ Move,
+ /**
+ * Line curve, the path segment contains 2 points indicating the two extremities of
+ * the line. The weight is set 0.0f and not meaningful.
+ */
+ Line,
+ /**
+ * Quadratic curve, the path segment contains 3 points in the following order:
+ * - Start point
+ * - Control point
+ * - End point
+ *
+ * The weight is set 0.0f and not meaningful.
+ */
+ Quadratic,
+ /**
+ * Conic curve, the path segment contains 3 points in the following order:
+ * - Start point
+ * - Control point
+ * - End point
+ *
+ * The curve is weighted by the [weight][PathSegment.weight] property.
+ */
+ Conic,
+ /**
+ * Cubic curve, the path segment contains 4 points in the following order:
+ * - Start point
+ * - First control point
+ * - Second control point
+ * - End point
+ *
+ * The weight is set 0.0f and not meaningful.
+ */
+ Cubic,
+ /**
+ * Close command, close the current contour by joining the last point added to the
+ * path with the first point of the current contour. The segment does not contain
+ * any point. The weight is set 0.0f and not meaningful.
+ */
+ Close,
+ /**
+ * Done command, which indicates that no further segment will be
+ * found in the path. It typically indicates the end of an iteration over a path
+ * and can be ignored.
+ */
+ Done
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as PathSegment
+
+ if (type != other.type) return false
+ if (!points.contentEquals(other.points)) return false
+ if (weight != other.weight) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = type.hashCode()
+ result = 31 * result + points.contentHashCode()
+ result = 31 * result + weight.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "PathSegment(type=$type, points=${points.contentToString()}, weight=$weight)"
+ }
+}
+
+/**
+ * A [PathSegment] containing the [Done][PathSegment.Type.Done] command.
+ * This static object exists to avoid allocating a new segment when returning a
+ * [Done][PathSegment.Type.Done] result from [PathIterator.next].
+ */
+val DoneSegment = PathSegment(PathSegment.Type.Done, emptyArray(), 0.0f)
+
+/**
+ * A [PathSegment] containing the [Close][PathSegment.Type.Close] command.
+ * This static object exists to avoid allocating a new segment when returning a
+ * [Close][PathSegment.Type.Close] result from [PathIterator.next].
+ */
+val CloseSegment = PathSegment(PathSegment.Type.Close, emptyArray(), 0.0f)
+
+/**
+ * Cache of [PathSegment.Type] values to avoid internal allocation on each use.
+ */
+internal val pathSegmentTypes = PathSegment.Type.values()
\ No newline at end of file
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 138be5e..528ada0 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -16,9 +16,6 @@
method public default static int getSdkStatus(android.content.Context context);
method public default static int getSdkStatus(android.content.Context context, optional String providerPackageName);
method public suspend Object? insertRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.InsertRecordsResponse>);
- method @Deprecated public default static boolean isApiSupported();
- method @Deprecated public default static boolean isProviderAvailable(android.content.Context context);
- method @Deprecated public default static boolean isProviderAvailable(android.content.Context context, optional String providerPackageName);
method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecord(kotlin.reflect.KClass<T> recordType, String recordId, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordResponse<T>>);
method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecords(androidx.health.connect.client.request.ReadRecordsRequest<T> request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordsResponse<T>>);
method public suspend Object? updateRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -36,9 +33,6 @@
method public androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context, optional String providerPackageName);
method public int getSdkStatus(android.content.Context context);
method public int getSdkStatus(android.content.Context context, optional String providerPackageName);
- method @Deprecated public boolean isApiSupported();
- method @Deprecated public boolean isProviderAvailable(android.content.Context context);
- method @Deprecated public boolean isProviderAvailable(android.content.Context context, optional String providerPackageName);
property public final String ACTION_HEALTH_CONNECT_SETTINGS;
field public static final int SDK_AVAILABLE = 3; // 0x3
field public static final int SDK_UNAVAILABLE = 1; // 0x1
@@ -46,16 +40,16 @@
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface PermissionController {
- method public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
- method public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<? extends java.lang.String>>);
method public suspend Object? revokeAllPermissions(kotlin.coroutines.Continuation<? super kotlin.Unit>);
field public static final androidx.health.connect.client.PermissionController.Companion Companion;
}
public static final class PermissionController.Companion {
- method public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
- method public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
}
}
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index 4da244d..b5ccae2 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -16,9 +16,6 @@
method public default static int getSdkStatus(android.content.Context context);
method public default static int getSdkStatus(android.content.Context context, optional String providerPackageName);
method public suspend Object? insertRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.InsertRecordsResponse>);
- method @Deprecated public default static boolean isApiSupported();
- method @Deprecated public default static boolean isProviderAvailable(android.content.Context context);
- method @Deprecated public default static boolean isProviderAvailable(android.content.Context context, optional String providerPackageName);
method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecord(kotlin.reflect.KClass<T> recordType, String recordId, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordResponse<T>>);
method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecords(androidx.health.connect.client.request.ReadRecordsRequest<T> request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordsResponse<T>>);
method public suspend Object? updateRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -36,9 +33,6 @@
method public androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context, optional String providerPackageName);
method public int getSdkStatus(android.content.Context context);
method public int getSdkStatus(android.content.Context context, optional String providerPackageName);
- method @Deprecated public boolean isApiSupported();
- method @Deprecated public boolean isProviderAvailable(android.content.Context context);
- method @Deprecated public boolean isProviderAvailable(android.content.Context context, optional String providerPackageName);
property public final String ACTION_HEALTH_CONNECT_SETTINGS;
field public static final int SDK_AVAILABLE = 3; // 0x3
field public static final int SDK_UNAVAILABLE = 1; // 0x1
@@ -46,16 +40,16 @@
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface PermissionController {
- method public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
- method public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public default static androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<? extends java.lang.String>>);
method public suspend Object? revokeAllPermissions(kotlin.coroutines.Continuation<? super kotlin.Unit>);
field public static final androidx.health.connect.client.PermissionController.Companion Companion;
}
public static final class PermissionController.Companion {
- method public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
- method public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract();
+ method @androidx.core.os.BuildCompat.PrereleaseSdkCheck public androidx.activity.result.contract.ActivityResultContract<java.util.Set<java.lang.String>,java.util.Set<java.lang.String>> createRequestPermissionResultContract(optional String providerPackageName);
}
}
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index f3b4530..16260ad 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -40,6 +40,7 @@
implementation(libs.guavaAndroid)
implementation(libs.kotlinCoroutinesAndroid)
implementation(libs.kotlinCoroutinesGuava)
+ implementation("androidx.core:core-ktx:1.8.0")
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
@@ -55,6 +56,13 @@
testImplementation(libs.espressoIntents)
testImplementation(libs.kotlinReflect)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.kotlinCoroutinesTest)
+ androidTestImplementation(libs.kotlinReflect)
+ androidTestImplementation(libs.kotlinTest)
+ androidTestImplementation(libs.junit)
+ androidTestImplementation(libs.truth)
+
samples(project(":health:connect:connect-client-samples"))
}
diff --git a/health/connect/connect-client/lint-baseline.xml b/health/connect/connect-client/lint-baseline.xml
index ecd3e59..ce944d0 100644
--- a/health/connect/connect-client/lint-baseline.xml
+++ b/health/connect/connect-client/lint-baseline.xml
@@ -11,6 +11,42 @@
</issue>
<issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/health/connect/client/PermissionController.kt"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public ClientConfiguration(String apiClientName, String servicePackageName, String bindAction) {"
diff --git a/health/connect/connect-client/src/androidTest/AndroidManifest.xml b/health/connect/connect-client/src/androidTest/AndroidManifest.xml
index 4d68dc2..34efdec 100644
--- a/health/connect/connect-client/src/androidTest/AndroidManifest.xml
+++ b/health/connect/connect-client/src/androidTest/AndroidManifest.xml
@@ -15,5 +15,96 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Read permissions for ACTIVITY. -->
+ <uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED"/>
+ <uses-permission android:name="android.permission.health.READ_DISTANCE"/>
+ <uses-permission android:name="android.permission.health.READ_ELEVATION_GAINED"/>
+ <uses-permission android:name="android.permission.health.READ_EXERCISE"/>
+ <uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED"/>
+ <uses-permission android:name="android.permission.health.READ_STEPS"/>
+ <uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED"/>
+ <uses-permission android:name="android.permission.health.READ_VO2_MAX"/>
+ <uses-permission android:name="android.permission.health.READ_WHEELCHAIR_PUSHES"/>
+ <uses-permission android:name="android.permission.health.READ_POWER"/>
+ <uses-permission android:name="android.permission.health.READ_SPEED"/>
+ <!-- Read permissions for BODY_MEASUREMENTS. -->
+ <uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE"/>
+ <uses-permission android:name="android.permission.health.READ_BODY_FAT"/>
+ <uses-permission android:name="android.permission.health.READ_BODY_WATER_MASS"/>
+ <uses-permission android:name="android.permission.health.READ_BONE_MASS"/>
+ <uses-permission android:name="android.permission.health.READ_HEIGHT"/>
+ <uses-permission android:name="android.permission.health.READ_LEAN_BODY_MASS"/>
+ <uses-permission android:name="android.permission.health.READ_WEIGHT"/>
+
+ <!-- Read permissions for CYCLE_TRACKING. -->
+ <uses-permission android:name="android.permission.health.READ_CERVICAL_MUCUS"/>
+ <uses-permission android:name="android.permission.health.READ_MENSTRUATION"/>
+ <uses-permission android:name="android.permission.health.READ_OVULATION_TEST"/>
+ <uses-permission android:name="android.permission.health.READ_SEXUAL_ACTIVITY"/>
+
+ <!-- Read permissions for NUTRITION. -->
+ <uses-permission android:name="android.permission.health.READ_HYDRATION"/>
+ <uses-permission android:name="android.permission.health.READ_NUTRITION"/>
+
+ <!-- Read permissions for SLEEP. -->
+ <uses-permission android:name="android.permission.health.READ_SLEEP"/>
+
+ <!-- Read permissions for VITALS. -->
+ <uses-permission android:name="android.permission.health.READ_BASAL_BODY_TEMPERATURE"/>
+ <uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE"/>
+ <uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE"/>
+ <uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE"/>
+ <uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
+ <uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY"/>
+ <uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION"/>
+ <uses-permission android:name="android.permission.health.READ_RESPIRATORY_RATE"/>
+ <uses-permission android:name="android.permission.health.READ_RESTING_HEART_RATE"/>
+
+ <!-- Write permissions for ACTIVITY. -->
+ <uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED"/>
+ <uses-permission android:name="android.permission.health.WRITE_DISTANCE"/>
+ <uses-permission android:name="android.permission.health.WRITE_ELEVATION_GAINED"/>
+ <uses-permission android:name="android.permission.health.WRITE_EXERCISE"/>
+ <uses-permission android:name="android.permission.health.WRITE_FLOORS_CLIMBED"/>
+ <uses-permission android:name="android.permission.health.WRITE_STEPS"/>
+ <uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED"/>
+ <uses-permission android:name="android.permission.health.WRITE_VO2_MAX"/>
+ <uses-permission android:name="android.permission.health.WRITE_WHEELCHAIR_PUSHES"/>
+ <uses-permission android:name="android.permission.health.WRITE_POWER"/>
+ <uses-permission android:name="android.permission.health.WRITE_SPEED"/>
+
+ <!-- Write permissions for BODY_MEASUREMENTS. -->
+ <uses-permission android:name="android.permission.health.WRITE_BASAL_METABOLIC_RATE"/>
+ <uses-permission android:name="android.permission.health.WRITE_BODY_FAT"/>
+ <uses-permission android:name="android.permission.health.WRITE_BODY_WATER_MASS"/>
+ <uses-permission android:name="android.permission.health.WRITE_BONE_MASS"/>
+ <uses-permission android:name="android.permission.health.WRITE_HEIGHT"/>
+ <uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS"/>
+ <uses-permission android:name="android.permission.health.WRITE_WEIGHT"/>
+
+ <!-- Write permissions for CYCLE_TRACKING. -->
+ <uses-permission android:name="android.permission.health.WRITE_CERVICAL_MUCUS"/>
+ <uses-permission android:name="android.permission.health.WRITE_INTERMENSTRUAL_BLEEDING"/>
+ <uses-permission android:name="android.permission.health.WRITE_MENSTRUATION"/>
+ <uses-permission android:name="android.permission.health.WRITE_OVULATION_TEST"/>
+ <uses-permission android:name="android.permission.health.WRITE_SEXUAL_ACTIVITY"/>
+
+ <!-- Write permissions for NUTRITION. -->
+ <uses-permission android:name="android.permission.health.WRITE_HYDRATION"/>
+ <uses-permission android:name="android.permission.health.WRITE_NUTRITION"/>
+
+ <!-- Write permissions for SLEEP. -->
+ <uses-permission android:name="android.permission.health.WRITE_SLEEP"/>
+
+ <!-- Write permissions for VITALS. -->
+ <uses-permission android:name="android.permission.health.WRITE_BASAL_BODY_TEMPERATURE"/>
+ <uses-permission android:name="android.permission.health.WRITE_BLOOD_GLUCOSE"/>
+ <uses-permission android:name="android.permission.health.WRITE_BLOOD_PRESSURE"/>
+ <uses-permission android:name="android.permission.health.WRITE_BODY_TEMPERATURE"/>
+ <uses-permission android:name="android.permission.health.WRITE_HEART_RATE"/>
+ <uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY"/>
+ <uses-permission android:name="android.permission.health.WRITE_OXYGEN_SATURATION"/>
+ <uses-permission android:name="android.permission.health.WRITE_RESPIRATORY_RATE"/>
+ <uses-permission android:name="android.permission.health.WRITE_RESTING_HEART_RATE"/>
</manifest>
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/ClassFinder.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/ClassFinder.kt
new file mode 100644
index 0000000..5539e49
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/ClassFinder.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client
+
+import androidx.health.connect.client.records.Record
+import java.io.File
+import java.net.URL
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import kotlin.reflect.KClass
+
+@Suppress("UNCHECKED_CAST")
+val RECORD_CLASSES: List<KClass<out Record>> by lazy {
+ findClasses("androidx.health.connect.client.records")
+ .filterNot { it.java.isInterface }
+ .filter { it.simpleName.orEmpty().endsWith("Record") }
+ .map { it as KClass<out Record> }
+}
+
+fun findClasses(packageName: String): Set<KClass<*>> {
+ val resources =
+ requireNotNull(Thread.currentThread().contextClassLoader)
+ .getResources(packageName.replace('.', '/'))
+
+ return buildSet {
+ while (resources.hasMoreElements()) {
+ val classNames = findClasses(resources.nextElement().file, packageName)
+ for (className in classNames) {
+ add(Class.forName(className).kotlin)
+ }
+ }
+ }
+}
+
+private fun findClasses(directory: String, packageName: String): Set<String> = buildSet {
+ if (directory.startsWith("file:") && ('!' in directory)) {
+ addAll(unzipClasses(path = directory, packageName = packageName))
+ }
+
+ for (file in File(directory).takeIf(File::exists)?.listFiles() ?: emptyArray()) {
+ if (file.isDirectory) {
+ addAll(findClasses(file.absolutePath, "$packageName.${file.name}"))
+ } else if (file.name.endsWith(".class")) {
+ add("$packageName.${file.name.dropLast(6)}")
+ }
+ }
+}
+
+private fun unzipClasses(path: String, packageName: String): Set<String> =
+ ZipInputStream(URL(path.substringBefore('!')).openStream()).use { zip ->
+ buildSet {
+ while (true) {
+ val entry = zip.nextEntry ?: break
+ val className = entry.formatClassName()
+ if ((className != null) && className.startsWith(packageName)) {
+ add(className)
+ }
+ }
+ }
+ }
+
+private fun ZipEntry.formatClassName(): String? =
+ name
+ .takeIf { it.endsWith(".class") }
+ ?.replace("[$].*".toRegex(), "")
+ ?.replace("[.]class".toRegex(), "")
+ ?.replace('/', '.')
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImplTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImplTest.kt
new file mode 100644
index 0000000..3cccbb7
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImplTest.kt
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl
+
+import android.annotation.TargetApi
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.RemoteException
+import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.changes.DeletionChange
+import androidx.health.connect.client.changes.UpsertionChange
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_PREFIX
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.WeightRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.request.AggregateGroupByDurationRequest
+import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ChangesTokenRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
+import androidx.health.connect.client.time.TimeRangeFilter
+import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.Mass
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.GrantPermissionRule
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.time.Instant
+import java.time.LocalDateTime
+import java.time.Period
+import java.time.ZoneOffset
+import kotlin.test.assertFailsWith
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@MediumTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class HealthConnectClientUpsideDownImplTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val allHealthPermissions =
+ context.packageManager
+ .getPackageInfo(
+ context.packageName,
+ PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong())
+ )
+ .requestedPermissions
+ .filter { it.startsWith(PERMISSION_PREFIX) }
+ .toTypedArray()
+
+ // Grant every permission as deletion by id checks for every permission
+ @get:Rule
+ val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(*allHealthPermissions)
+
+ private lateinit var healthConnectClient: HealthConnectClient
+
+ @Before
+ fun setUp() {
+ healthConnectClient = HealthConnectClientUpsideDownImpl(context)
+ }
+
+ @After
+ fun tearDown() = runTest {
+ healthConnectClient.deleteRecords(StepsRecord::class, TimeRangeFilter.none())
+ healthConnectClient.deleteRecords(HeartRateRecord::class, TimeRangeFilter.none())
+ healthConnectClient.deleteRecords(NutritionRecord::class, TimeRangeFilter.none())
+ }
+
+ @Test
+ fun insertRecords() = runTest {
+ val response =
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = null
+ )
+ )
+ )
+ assertThat(response.recordIdsList).hasSize(1)
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun deleteRecords_byId() = runTest {
+ val recordIds =
+ healthConnectClient
+ .insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = null
+ ),
+ StepsRecord(
+ count = 15,
+ startTime = Instant.ofEpochMilli(12340L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(56780L),
+ endZoneOffset = null
+ ),
+ StepsRecord(
+ count = 20,
+ startTime = Instant.ofEpochMilli(123400L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(567800L),
+ endZoneOffset = null,
+ metadata = Metadata(clientRecordId = "clientId")
+ ),
+ )
+ )
+ .recordIdsList
+
+ val initialRecords =
+ healthConnectClient
+ .readRecords(ReadRecordsRequest(StepsRecord::class, TimeRangeFilter.none()))
+ .records
+
+ healthConnectClient.deleteRecords(
+ StepsRecord::class,
+ listOf(recordIds[1]),
+ listOf("clientId")
+ )
+
+ assertThat(
+ healthConnectClient
+ .readRecords(ReadRecordsRequest(StepsRecord::class, TimeRangeFilter.none()))
+ .records
+ )
+ .containsExactly(initialRecords[0])
+ }
+
+ // TODO(b/264253708): remove @Ignore from this test case once bug is resolved
+ @Test
+ @Ignore("Blocked while investigating b/264253708")
+ fun deleteRecords_byTimeRange() = runTest {
+ healthConnectClient
+ .insertRecords(
+ listOf(
+ StepsRecord(
+ count = 100,
+ startTime = Instant.ofEpochMilli(1_234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5_678L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 150,
+ startTime = Instant.ofEpochMilli(12_340L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(56_780L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ )
+ )
+ .recordIdsList
+
+ val initialRecords =
+ healthConnectClient
+ .readRecords(ReadRecordsRequest(StepsRecord::class, TimeRangeFilter.none()))
+ .records
+
+ healthConnectClient.deleteRecords(
+ StepsRecord::class,
+ TimeRangeFilter.before(Instant.ofEpochMilli(10_000L))
+ )
+
+ assertThat(
+ healthConnectClient
+ .readRecords(ReadRecordsRequest(StepsRecord::class, TimeRangeFilter.none()))
+ .records
+ )
+ .containsExactly(initialRecords[1])
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun updateRecords() = runTest {
+ val id =
+ healthConnectClient
+ .insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = null
+ )
+ )
+ )
+ .recordIdsList[0]
+
+ val insertedRecord = healthConnectClient.readRecord(StepsRecord::class, id).record
+
+ healthConnectClient.updateRecords(
+ listOf(
+ StepsRecord(
+ count = 5,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = null,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = null,
+ metadata = Metadata(id, insertedRecord.metadata.dataOrigin)
+ )
+ )
+ )
+
+ val updatedRecord = healthConnectClient.readRecord(StepsRecord::class, id).record
+
+ assertThat(updatedRecord.count).isEqualTo(5L)
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun readRecord_withId() = runTest {
+ val insertResponse =
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC
+ )
+ )
+ )
+
+ val readResponse =
+ healthConnectClient.readRecord(StepsRecord::class, insertResponse.recordIdsList[0])
+
+ with(readResponse.record) {
+ assertThat(count).isEqualTo(10)
+ assertThat(startTime).isEqualTo(Instant.ofEpochMilli(1234L))
+ assertThat(startZoneOffset).isEqualTo(ZoneOffset.UTC)
+ assertThat(endTime).isEqualTo(Instant.ofEpochMilli(5678L))
+ assertThat(endZoneOffset).isEqualTo(ZoneOffset.UTC)
+ }
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun readRecords_withFilters() = runTest {
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 5,
+ startTime = Instant.ofEpochMilli(12340L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(56780L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ )
+ )
+
+ val readResponse =
+ healthConnectClient.readRecords(
+ ReadRecordsRequest(
+ StepsRecord::class,
+ TimeRangeFilter.after(Instant.ofEpochMilli(10_000L))
+ )
+ )
+
+ assertThat(readResponse.records[0].count).isEqualTo(5)
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun readRecord_noRecords_throwRemoteException() = runTest {
+ assertFailsWith<RemoteException> { healthConnectClient.readRecord(StepsRecord::class, "1") }
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun aggregateRecords() = runTest {
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 5,
+ startTime = Instant.ofEpochMilli(12340L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(56780L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ HeartRateRecord(
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC,
+ samples =
+ listOf(
+ HeartRateRecord.Sample(Instant.ofEpochMilli(1234L), 57L),
+ HeartRateRecord.Sample(Instant.ofEpochMilli(1235L), 120L)
+ )
+ ),
+ HeartRateRecord(
+ startTime = Instant.ofEpochMilli(12340L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(56780L),
+ endZoneOffset = ZoneOffset.UTC,
+ samples =
+ listOf(
+ HeartRateRecord.Sample(Instant.ofEpochMilli(12340L), 47L),
+ HeartRateRecord.Sample(Instant.ofEpochMilli(12350L), 48L)
+ )
+ ),
+ NutritionRecord(
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC,
+ energy = Energy.kilocalories(200.0)
+ ),
+ WeightRecord(
+ time = Instant.ofEpochMilli(1234L),
+ zoneOffset = ZoneOffset.UTC,
+ weight = Mass.kilograms(100.0)
+ ),
+ )
+ )
+
+ val aggregateResponse =
+ healthConnectClient.aggregate(
+ AggregateRequest(
+ setOf(
+ StepsRecord.COUNT_TOTAL,
+ HeartRateRecord.BPM_MIN,
+ HeartRateRecord.BPM_MAX,
+ NutritionRecord.ENERGY_TOTAL,
+ NutritionRecord.CAFFEINE_TOTAL,
+ WeightRecord.WEIGHT_MAX,
+ WheelchairPushesRecord.COUNT_TOTAL,
+ ),
+ TimeRangeFilter.none()
+ )
+ )
+
+ with(aggregateResponse) {
+ assertThat(this[StepsRecord.COUNT_TOTAL]).isEqualTo(15L)
+ assertThat(this[HeartRateRecord.BPM_MIN]).isEqualTo(47L)
+ assertThat(this[HeartRateRecord.BPM_MAX]).isEqualTo(120L)
+ assertThat(this[NutritionRecord.ENERGY_TOTAL]).isEqualTo(Energy.kilocalories(200.0))
+ assertThat(this[NutritionRecord.CAFFEINE_TOTAL]).isEqualTo(Mass.grams(0.0))
+ assertThat(this[WeightRecord.WEIGHT_MAX]).isEqualTo(Mass.kilograms(100.0))
+
+ assertThat(contains(WheelchairPushesRecord.COUNT_TOTAL)).isFalse()
+ }
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun aggregateRecordsGroupByDuration() = runTest {
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 1,
+ startTime = Instant.ofEpochMilli(1200L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(1240L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 2,
+ startTime = Instant.ofEpochMilli(1300L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(1500L),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 5,
+ startTime = Instant.ofEpochMilli(2400L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(3500L),
+ endZoneOffset = ZoneOffset.UTC
+ )
+ )
+ )
+
+ val aggregateResponse =
+ healthConnectClient.aggregateGroupByDuration(
+ AggregateGroupByDurationRequest(
+ setOf(StepsRecord.COUNT_TOTAL),
+ TimeRangeFilter.between(
+ Instant.ofEpochMilli(1000L),
+ Instant.ofEpochMilli(3000L)
+ ),
+ Duration.ofMillis(1000),
+ setOf()
+ )
+ )
+
+ with(aggregateResponse) {
+ assertThat(this).hasSize(2)
+ assertThat(this[0].result[StepsRecord.COUNT_TOTAL]).isEqualTo(3)
+ assertThat(this[1].result[StepsRecord.COUNT_TOTAL]).isEqualTo(5)
+ }
+ }
+
+ @Test
+ @Ignore("Blocked as period response from platform has a bug with inverted start/end timestamps")
+ fun aggregateRecordsGroupByPeriod() = runTest {
+ healthConnectClient.insertRecords(
+ listOf(
+ StepsRecord(
+ count = 100,
+ startTime = LocalDateTime.of(2018, 10, 11, 7, 10).toInstant(ZoneOffset.UTC),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = LocalDateTime.of(2018, 10, 11, 7, 15).toInstant(ZoneOffset.UTC),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 200,
+ startTime = LocalDateTime.of(2018, 10, 11, 10, 10).toInstant(ZoneOffset.UTC),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = LocalDateTime.of(2018, 10, 11, 11, 0).toInstant(ZoneOffset.UTC),
+ endZoneOffset = ZoneOffset.UTC
+ ),
+ StepsRecord(
+ count = 50,
+ startTime = LocalDateTime.of(2018, 10, 13, 7, 10).toInstant(ZoneOffset.UTC),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = LocalDateTime.of(2018, 10, 13, 8, 10).toInstant(ZoneOffset.UTC),
+ endZoneOffset = ZoneOffset.UTC
+ )
+ )
+ )
+
+ val aggregateResponse =
+ healthConnectClient.aggregateGroupByPeriod(
+ AggregateGroupByPeriodRequest(
+ setOf(StepsRecord.COUNT_TOTAL),
+ TimeRangeFilter.between(
+ LocalDateTime.of(2018, 10, 11, 6, 10).toInstant(ZoneOffset.UTC),
+ LocalDateTime.of(2018, 10, 12, 7, 15).toInstant(ZoneOffset.UTC),
+ ),
+ timeRangeSlicer = Period.ofDays(1)
+ )
+ )
+
+ with(aggregateResponse) {
+ assertThat(this).hasSize(2)
+ assertThat(this[0].result[StepsRecord.COUNT_TOTAL]).isEqualTo(300)
+ assertThat(this[1].result[StepsRecord.COUNT_TOTAL]).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun getChangesToken() = runTest {
+ val token =
+ healthConnectClient.getChangesToken(
+ ChangesTokenRequest(setOf(StepsRecord::class), setOf())
+ )
+ assertThat(token).isNotEmpty()
+ }
+
+ @Test
+ @Ignore("b/270954533")
+ fun getChanges() = runTest {
+ val token =
+ healthConnectClient.getChangesToken(
+ ChangesTokenRequest(setOf(StepsRecord::class), setOf())
+ )
+
+ val insertedRecordId =
+ healthConnectClient
+ .insertRecords(
+ listOf(
+ StepsRecord(
+ count = 10,
+ startTime = Instant.ofEpochMilli(1234L),
+ startZoneOffset = ZoneOffset.UTC,
+ endTime = Instant.ofEpochMilli(5678L),
+ endZoneOffset = ZoneOffset.UTC
+ )
+ )
+ )
+ .recordIdsList[0]
+
+ val record = healthConnectClient.readRecord(StepsRecord::class, insertedRecordId).record
+
+ assertThat(healthConnectClient.getChanges(token).changes)
+ .containsExactly(UpsertionChange(record))
+
+ healthConnectClient.deleteRecords(StepsRecord::class, TimeRangeFilter.none())
+
+ assertThat(healthConnectClient.getChanges(token).changes)
+ .containsExactly(DeletionChange(insertedRecordId))
+ }
+
+ @Test
+ fun getGrantedPermissions() = runTest {
+ assertThat(healthConnectClient.permissionController.getGrantedPermissions())
+ .containsExactlyElementsIn(allHealthPermissions)
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/PermissionControllerUpsideDownTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/PermissionControllerUpsideDownTest.kt
new file mode 100644
index 0000000..e364fd6
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/PermissionControllerUpsideDownTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl
+
+import android.annotation.TargetApi
+import android.health.connect.HealthPermissions
+import android.os.Build
+import androidx.health.connect.client.PermissionController
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_PREFIX
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.GrantPermissionRule
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@MediumTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class PermissionControllerUpsideDownTest {
+
+ @get:Rule
+ val grantPermissionRule: GrantPermissionRule =
+ GrantPermissionRule.grant(HealthPermissions.WRITE_STEPS, HealthPermissions.READ_DISTANCE)
+
+ @Test
+ fun getGrantedPermissions() = runTest {
+ val permissionController: PermissionController =
+ HealthConnectClientUpsideDownImpl(ApplicationProvider.getApplicationContext())
+ // Permissions may have been granted by the other instrumented test in this directory.
+ // Since there is no way to revoke permissions with grantPermissionRule, use containsAtLeast
+ // instead of containsExactly.
+ assertThat(permissionController.getGrantedPermissions())
+ .containsAtLeast(HealthPermissions.WRITE_STEPS, HealthPermissions.READ_DISTANCE)
+ }
+
+ @Test
+ fun revokeAllPermissions_revokesHealthPermissions() = runTest {
+ val revokedPermissions: MutableList<String> = mutableListOf()
+ val permissionController: PermissionController =
+ HealthConnectClientUpsideDownImpl(ApplicationProvider.getApplicationContext()) {
+ permissionsToRevoke ->
+ revokedPermissions.addAll(permissionsToRevoke)
+ }
+ permissionController.revokeAllPermissions()
+ assertThat(revokedPermissions.all { it.startsWith(PERMISSION_PREFIX) }).isTrue()
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
new file mode 100644
index 0000000..ea4548d
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl
+
+import android.annotation.TargetApi
+import android.content.Context
+import android.content.Intent
+import android.health.connect.HealthConnectManager
+import android.os.Build
+import androidx.health.connect.client.impl.platform.records.PlatformExerciseRoute
+import androidx.health.connect.client.impl.platform.records.PlatformExerciseRouteLocationBuilder
+import androidx.health.connect.client.impl.platform.records.PlatformLength
+import androidx.health.connect.client.permission.platform.RequestExerciseRouteUpsideDownCake
+import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.units.Length
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class RequestExerciseRouteUpsideDownCakeTest {
+
+ private lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ }
+
+ @Test
+ fun createIntentTest() {
+ val requestRouteContract = RequestExerciseRouteUpsideDownCake()
+ val intent = requestRouteContract.createIntent(context, "someUid")
+ assertThat(intent.action).isEqualTo("android.health.connect.action.REQUEST_EXERCISE_ROUTE")
+ assertThat(intent.getStringExtra("android.health.connect.extra.SESSION_ID"))
+ .isEqualTo("someUid")
+ }
+
+ @Test
+ fun parseIntent_null() {
+ val requestRouteContract = RequestExerciseRouteUpsideDownCake()
+ val result = requestRouteContract.parseResult(0, null)
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun parseIntent_emptyIntent() {
+ val requestRouteContract = RequestExerciseRouteUpsideDownCake()
+ val result = requestRouteContract.parseResult(0, Intent())
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun parseIntent_emptyRoute() {
+ val requestRouteContract = RequestExerciseRouteUpsideDownCake()
+ val intent = Intent()
+ intent.putExtra(HealthConnectManager.EXTRA_EXERCISE_ROUTE, PlatformExerciseRoute(listOf()))
+ val result = requestRouteContract.parseResult(0, intent)
+ assertThat(result).isEqualTo(ExerciseRoute(listOf()))
+ }
+
+ @Test
+ fun parseIntent() {
+ val requestRouteContract = RequestExerciseRouteUpsideDownCake()
+ val intent = Intent()
+ val location1 =
+ PlatformExerciseRouteLocationBuilder(Instant.ofEpochMilli(1234L), 23.4, -23.4)
+ .setAltitude(PlatformLength.fromMeters(12.3))
+ .setHorizontalAccuracy(PlatformLength.fromMeters(0.9))
+ .setVerticalAccuracy(PlatformLength.fromMeters(0.3))
+ .build()
+ val location2 =
+ PlatformExerciseRouteLocationBuilder(Instant.ofEpochMilli(3456L), 23.45, -23.45).build()
+
+ intent.putExtra(
+ HealthConnectManager.EXTRA_EXERCISE_ROUTE,
+ PlatformExerciseRoute(listOf(location1, location2))
+ )
+ val result = requestRouteContract.parseResult(0, intent)
+ assertThat(result)
+ .isEqualTo(
+ ExerciseRoute(
+ listOf(
+ ExerciseRoute.Location(
+ time = Instant.ofEpochMilli(1234L),
+ latitude = 23.4,
+ longitude = -23.4,
+ horizontalAccuracy = Length.meters(0.9),
+ verticalAccuracy = Length.meters(0.3),
+ altitude = Length.meters(12.3)
+ ),
+ ExerciseRoute.Location(
+ time = Instant.ofEpochMilli(3456L),
+ latitude = 23.45,
+ longitude = -23.45,
+ )
+ )
+ )
+ )
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/MetadataConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/MetadataConvertersTest.kt
new file mode 100644
index 0000000..af0b765
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/MetadataConvertersTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.annotation.TargetApi
+import android.os.Build
+import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.records.metadata.Device
+import androidx.health.connect.client.records.metadata.Metadata
+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 java.time.Instant
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@SmallTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class MetadataConvertersTest {
+
+ fun metadata_convertToPlatform() {
+ val metadata =
+ Metadata(
+ id = "someId",
+ dataOrigin = DataOrigin("origin package name"),
+ lastModifiedTime = Instant.ofEpochMilli(6666L),
+ clientRecordId = "clientId",
+ clientRecordVersion = 2L,
+ recordingMethod = Metadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED,
+ device =
+ Device(
+ manufacturer = "Awesome-watches",
+ model = "AwesomeOne",
+ type = Device.TYPE_WATCH
+ )
+ )
+
+ with(metadata.toPlatformMetadata()) {
+ assertThat(id).isEqualTo("someId")
+ assertThat(dataOrigin)
+ .isEqualTo(
+ PlatformDataOriginBuilder().setPackageName("origin package name").build()
+ )
+ assertThat(clientRecordId).isEqualTo("clientId")
+ assertThat(clientRecordVersion).isEqualTo(2L)
+ assertThat(recordingMethod)
+ .isEqualTo(PlatformMetadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED)
+ assertThat(device)
+ .isEqualTo(
+ PlatformDeviceBuilder()
+ .setManufacturer("Awesome-watches")
+ .setModel("AwesomeOne")
+ .setType(PlatformDevice.DEVICE_TYPE_WATCH)
+ .build()
+ )
+ }
+ }
+
+ @Test
+ fun metadata_convertToPlatform_noDevice() {
+ val metadata =
+ Metadata(
+ id = "someId",
+ dataOrigin = DataOrigin("origin package name"),
+ lastModifiedTime = Instant.ofEpochMilli(6666L),
+ clientRecordId = "clientId",
+ clientRecordVersion = 2L
+ )
+
+ with(metadata.toPlatformMetadata()) {
+ assertThat(id).isEqualTo("someId")
+ assertThat(dataOrigin)
+ .isEqualTo(
+ PlatformDataOriginBuilder().setPackageName("origin package name").build()
+ )
+ assertThat(clientRecordId).isEqualTo("clientId")
+ assertThat(clientRecordVersion).isEqualTo(2L)
+ assertThat(device).isEqualTo(PlatformDeviceBuilder().build())
+ }
+ }
+
+ @Test
+ fun metadata_convertToSdk() {
+ val metadata =
+ PlatformMetadataBuilder()
+ .apply {
+ setId("someId")
+ setDataOrigin(
+ PlatformDataOriginBuilder().setPackageName("origin package name").build()
+ )
+ setLastModifiedTime(Instant.ofEpochMilli(6666L))
+ setClientRecordId("clientId")
+ setClientRecordVersion(2L)
+ setRecordingMethod(PlatformMetadata.RECORDING_METHOD_MANUAL_ENTRY)
+ setDevice(
+ PlatformDeviceBuilder()
+ .setManufacturer("AwesomeTech")
+ .setModel("AwesomeTwo")
+ .setType(PlatformDevice.DEVICE_TYPE_WATCH)
+ .build()
+ )
+ }
+ .build()
+
+ with(metadata.toSdkMetadata()) {
+ assertThat(id).isEqualTo("someId")
+ assertThat(dataOrigin).isEqualTo(DataOrigin("origin package name"))
+ assertThat(lastModifiedTime).isEqualTo(Instant.ofEpochMilli(6666L))
+ assertThat(clientRecordId).isEqualTo("clientId")
+ assertThat(clientRecordVersion).isEqualTo(2L)
+ assertThat(recordingMethod).isEqualTo(Metadata.RECORDING_METHOD_MANUAL_ENTRY)
+ assertThat(device)
+ .isEqualTo(
+ Device(
+ manufacturer = "AwesomeTech",
+ model = "AwesomeTwo",
+ type = Device.TYPE_WATCH
+ )
+ )
+ }
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
new file mode 100644
index 0000000..892ed70
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
@@ -0,0 +1,1750 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.annotation.TargetApi
+import android.os.Build
+import androidx.health.connect.client.RECORD_CLASSES
+import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
+import androidx.health.connect.client.records.BasalBodyTemperatureRecord
+import androidx.health.connect.client.records.BasalMetabolicRateRecord
+import androidx.health.connect.client.records.BloodGlucoseRecord
+import androidx.health.connect.client.records.BloodPressureRecord
+import androidx.health.connect.client.records.BodyFatRecord
+import androidx.health.connect.client.records.BodyTemperatureMeasurementLocation
+import androidx.health.connect.client.records.BodyTemperatureRecord
+import androidx.health.connect.client.records.BodyWaterMassRecord
+import androidx.health.connect.client.records.BoneMassRecord
+import androidx.health.connect.client.records.CervicalMucusRecord
+import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.ElevationGainedRecord
+import androidx.health.connect.client.records.ExerciseLap
+import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseSegment
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.FloorsClimbedRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
+import androidx.health.connect.client.records.HeightRecord
+import androidx.health.connect.client.records.HydrationRecord
+import androidx.health.connect.client.records.InstantaneousRecord
+import androidx.health.connect.client.records.IntermenstrualBleedingRecord
+import androidx.health.connect.client.records.IntervalRecord
+import androidx.health.connect.client.records.LeanBodyMassRecord
+import androidx.health.connect.client.records.MealType
+import androidx.health.connect.client.records.MenstruationFlowRecord
+import androidx.health.connect.client.records.MenstruationPeriodRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.OvulationTestRecord
+import androidx.health.connect.client.records.OxygenSaturationRecord
+import androidx.health.connect.client.records.PowerRecord
+import androidx.health.connect.client.records.RespiratoryRateRecord
+import androidx.health.connect.client.records.RestingHeartRateRecord
+import androidx.health.connect.client.records.SexualActivityRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.SpeedRecord
+import androidx.health.connect.client.records.StepsCadenceRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
+import androidx.health.connect.client.records.Vo2MaxRecord
+import androidx.health.connect.client.records.WeightRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.BloodGlucose
+import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.Length
+import androidx.health.connect.client.units.Mass
+import androidx.health.connect.client.units.Percentage
+import androidx.health.connect.client.units.Power
+import androidx.health.connect.client.units.Pressure
+import androidx.health.connect.client.units.Temperature
+import androidx.health.connect.client.units.Velocity
+import androidx.health.connect.client.units.Volume
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import java.time.ZoneOffset
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@SmallTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class RecordConvertersTest {
+
+ private val tolerance = 1.0e-9
+
+ @Test
+ fun toPlatformRecordClass_supportsAllRecordTypes() {
+ RECORD_CLASSES.forEach { assertThat(it.toPlatformRecordClass()).isNotNull() }
+ }
+
+ @Test
+ fun stepsRecordClass_convertToPlatform() {
+ val stepsSdkClass = StepsRecord::class
+ val stepsPlatformClass = PlatformStepsRecord::class.java
+ assertThat(stepsSdkClass.toPlatformRecordClass()).isEqualTo(stepsPlatformClass)
+ }
+
+ @Test
+ fun activeCaloriesBurnedRecord_convertToPlatform() {
+ val platformActiveCaloriesBurned =
+ ActiveCaloriesBurnedRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ energy = Energy.calories(200.0),
+ )
+ .toPlatformRecord() as PlatformActiveCaloriesBurnedRecord
+
+ assertPlatformRecord(platformActiveCaloriesBurned) {
+ assertThat(energy).isEqualTo(PlatformEnergy.fromCalories(200.0))
+ }
+ }
+
+ @Test
+ fun basalBodyTemperatureRecord_convertToPlatform() {
+ val platformBasalBodyTemperature =
+ BasalBodyTemperatureRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ temperature = Temperature.celsius(37.0),
+ measurementLocation =
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FINGER
+ )
+ .toPlatformRecord() as PlatformBasalBodyTemperatureRecord
+
+ assertPlatformRecord(platformBasalBodyTemperature) {
+ assertThat(temperature).isEqualTo(PlatformTemperature.fromCelsius(37.0))
+ assertThat(measurementLocation)
+ .isEqualTo(PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FINGER)
+ }
+ }
+
+ @Test
+ fun basalMetabolicRateRecord_convertToPlatform() {
+ val platformBasalMetabolicRate =
+ BasalMetabolicRateRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ basalMetabolicRate = Power.watts(300.0),
+ )
+ .toPlatformRecord() as PlatformBasalMetabolicRateRecord
+
+ assertPlatformRecord(platformBasalMetabolicRate) {
+ assertThat(basalMetabolicRate).isEqualTo(PlatformPower.fromWatts(300.0))
+ }
+ }
+
+ @Test
+ fun bloodGlucoseRecord_convertToPlatform() {
+ val platformBloodGlucose =
+ BloodGlucoseRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ level = BloodGlucose.millimolesPerLiter(34.0),
+ specimenSource = BloodGlucoseRecord.SPECIMEN_SOURCE_TEARS,
+ mealType = MealType.MEAL_TYPE_BREAKFAST,
+ relationToMeal = BloodGlucoseRecord.RELATION_TO_MEAL_AFTER_MEAL,
+ )
+ .toPlatformRecord() as PlatformBloodGlucoseRecord
+
+ assertPlatformRecord(platformBloodGlucose) {
+ assertThat(level).isEqualTo(PlatformBloodGlucose.fromMillimolesPerLiter(34.0))
+ assertThat(specimenSource)
+ .isEqualTo(PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_TEARS)
+ assertThat(mealType).isEqualTo(PlatformMealType.MEAL_TYPE_BREAKFAST)
+ assertThat(relationToMeal)
+ .isEqualTo(PlatformBloodGlucoseRelationToMealType.RELATION_TO_MEAL_AFTER_MEAL)
+ }
+ }
+
+ @Test
+ fun bloodPressureRecord_convertToPlatform() {
+ val platformBloodPressure =
+ BloodPressureRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ systolic = Pressure.millimetersOfMercury(23.0),
+ diastolic = Pressure.millimetersOfMercury(24.0),
+ bodyPosition = BloodPressureRecord.BODY_POSITION_STANDING_UP,
+ measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+ )
+ .toPlatformRecord() as PlatformBloodPressureRecord
+
+ assertPlatformRecord(platformBloodPressure) {
+ assertThat(systolic).isEqualTo(PlatformPressure.fromMillimetersOfMercury(23.0))
+ assertThat(diastolic).isEqualTo(PlatformPressure.fromMillimetersOfMercury(24.0))
+ assertThat(bodyPosition)
+ .isEqualTo(PlatformBloodPressureBodyPosition.BODY_POSITION_STANDING_UP)
+ assertThat(measurementLocation)
+ .isEqualTo(
+ PlatformBloodPressureMeasurementLocation
+ .BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST
+ )
+ }
+ }
+
+ @Test
+ fun bodyFatRecord_convertToPlatform() {
+ val platformBodyFat =
+ BodyFatRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ percentage = Percentage(99.0),
+ )
+ .toPlatformRecord() as PlatformBodyFatRecord
+
+ assertPlatformRecord(platformBodyFat) {
+ assertThat(percentage).isEqualTo(PlatformPercentage.fromValue(99.0))
+ }
+ }
+
+ @Test
+ fun bodyTemperatureRecord_convertToPlatform() {
+ val platformBodyTemperature =
+ BodyTemperatureRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ temperature = Temperature.celsius(30.0),
+ measurementLocation =
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_ARMPIT,
+ )
+ .toPlatformRecord() as PlatformBodyTemperatureRecord
+
+ assertPlatformRecord(platformBodyTemperature) {
+ PlatformTemperature.fromCelsius(30.0)
+ assertThat(measurementLocation)
+ .isEqualTo(PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_ARMPIT)
+ }
+ }
+
+ @Test
+ fun bodyWaterMassRecord_convertToPlatform() {
+ val platformBodyWaterMass =
+ BodyWaterMassRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ mass = Mass.grams(40.0),
+ )
+ .toPlatformRecord() as PlatformBodyWaterMassRecord
+
+ assertPlatformRecord(platformBodyWaterMass) {
+ assertThat(bodyWaterMass).isEqualTo(PlatformMass.fromGrams(40.0))
+ }
+ }
+
+ @Test
+ fun boneMassRecord_convertToPlatform() {
+ val platformBoneMass =
+ BoneMassRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ mass = Mass.grams(5.0),
+ )
+ .toPlatformRecord() as PlatformBoneMassRecord
+
+ assertPlatformRecord(platformBoneMass) {
+ assertThat(mass).isEqualTo(PlatformMass.fromGrams(5.0))
+ }
+ }
+
+ @Test
+ fun cervicalMucusRecord_convertToPlatform() {
+ val platformCervicalMucus =
+ CervicalMucusRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ appearance = CervicalMucusRecord.APPEARANCE_CREAMY,
+ sensation = CervicalMucusRecord.SENSATION_LIGHT,
+ )
+ .toPlatformRecord() as PlatformCervicalMucusRecord
+
+ assertPlatformRecord(platformCervicalMucus) {
+ assertThat(appearance).isEqualTo(PlatformCervicalMucusAppearance.APPEARANCE_CREAMY)
+ assertThat(sensation).isEqualTo(PlatformCervicalMucusSensation.SENSATION_LIGHT)
+ }
+ }
+
+ @Test
+ fun cyclingPedalingCadenceRecord_convertToPlatform() {
+ val platformCyclingPedalingCadence =
+ CyclingPedalingCadenceRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ samples =
+ listOf(
+ CyclingPedalingCadenceRecord.Sample(START_TIME, 3.0),
+ CyclingPedalingCadenceRecord.Sample(END_TIME, 9.0)
+ ),
+ )
+ .toPlatformRecord() as PlatformCyclingPedalingCadenceRecord
+
+ assertPlatformRecord(platformCyclingPedalingCadence) {
+ assertThat(samples)
+ .comparingElementsUsing(
+ Correspondence.from<
+ PlatformCyclingPedalingCadenceSample, PlatformCyclingPedalingCadenceSample
+ >(
+ { actual, expected ->
+ actual!!.revolutionsPerMinute == expected!!.revolutionsPerMinute &&
+ actual.time == expected.time
+ },
+ "has same RPM and same time as"
+ )
+ )
+ .containsExactly(
+ PlatformCyclingPedalingCadenceSample(3.0, START_TIME),
+ PlatformCyclingPedalingCadenceSample(9.0, END_TIME)
+ )
+ }
+ }
+
+ @Test
+ fun distanceRecord_convertToPlatform() {
+ val platformDistance =
+ DistanceRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ distance = Length.meters(50.0),
+ )
+ .toPlatformRecord() as PlatformDistanceRecord
+
+ assertPlatformRecord(platformDistance) {
+ assertThat(distance).isEqualTo(PlatformLength.fromMeters(50.0))
+ }
+ }
+
+ @Test
+ fun elevationGainedRecord_convertToPlatform() {
+ val platformElevationGained =
+ ElevationGainedRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ elevation = Length.meters(10.0),
+ )
+ .toPlatformRecord() as PlatformElevationGainedRecord
+
+ assertPlatformRecord(platformElevationGained) {
+ assertThat(elevation).isEqualTo(PlatformLength.fromMeters(10.0))
+ }
+ }
+
+ @Test
+ fun exerciseSessionRecord_convertToPlatform() {
+ val platformExerciseSession =
+ ExerciseSessionRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ exerciseType =
+ ExerciseSessionRecord.EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+ title = "HIIT training",
+ notes = "Hard workout",
+ laps =
+ listOf(
+ ExerciseLap(
+ START_TIME.plusMillis(6),
+ START_TIME.plusMillis(10),
+ Length.meters(1.0)
+ ),
+ ExerciseLap(
+ START_TIME.plusMillis(11),
+ START_TIME.plusMillis(15),
+ Length.meters(1.5)
+ )
+ ),
+ segments =
+ listOf(
+ ExerciseSegment(
+ START_TIME.plusMillis(1),
+ START_TIME.plusMillis(10),
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+ 10
+ )
+ ),
+ route =
+ ExerciseRoute(
+ listOf(
+ ExerciseRoute.Location(
+ START_TIME,
+ latitude = 23.5,
+ longitude = -23.6,
+ altitude = Length.meters(20.0),
+ horizontalAccuracy = Length.meters(2.0),
+ verticalAccuracy = Length.meters(3.0)
+ )
+ )
+ )
+ )
+ .toPlatformRecord() as PlatformExerciseSessionRecord
+
+ assertPlatformRecord(platformExerciseSession) {
+ assertThat(title).isEqualTo("HIIT training")
+ assertThat(notes).isEqualTo("Hard workout")
+ assertThat(exerciseType)
+ .isEqualTo(
+ PlatformExerciseSessionType
+ .EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
+ )
+ assertThat(laps)
+ .containsExactly(
+ PlatformExerciseLapBuilder(START_TIME.plusMillis(6), START_TIME.plusMillis(10))
+ .setLength(PlatformLength.fromMeters(1.0))
+ .build(),
+ PlatformExerciseLapBuilder(START_TIME.plusMillis(11), START_TIME.plusMillis(15))
+ .setLength(PlatformLength.fromMeters(1.5))
+ .build()
+ )
+ assertThat(segments)
+ .containsExactly(
+ PlatformExerciseSegmentBuilder(
+ START_TIME.plusMillis(1),
+ START_TIME.plusMillis(10),
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS
+ )
+ .setRepetitionsCount(10)
+ .build()
+ )
+ assertThat(route)
+ .isEqualTo(
+ PlatformExerciseRoute(
+ listOf(
+ PlatformExerciseRouteLocationBuilder(START_TIME, 23.5, -23.6)
+ .setAltitude(PlatformLength.fromMeters(20.0))
+ .setHorizontalAccuracy(PlatformLength.fromMeters(2.0))
+ .setVerticalAccuracy(PlatformLength.fromMeters(3.0))
+ .build()
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun floorsClimbedRecord_convertToPlatform() {
+ val platformFloorsClimbed =
+ FloorsClimbedRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ floors = 3.9,
+ )
+ .toPlatformRecord() as PlatformFloorsClimbedRecord
+
+ assertPlatformRecord(platformFloorsClimbed) { assertThat(floors).isEqualTo(3.9) }
+ }
+
+ @Test
+ fun heartRateRecord_convertToPlatform() {
+ val heartRate =
+ HeartRateRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ samples =
+ listOf(
+ HeartRateRecord.Sample(Instant.ofEpochMilli(1234L), 55L),
+ HeartRateRecord.Sample(Instant.ofEpochMilli(5678L), 57L)
+ )
+ )
+
+ val platformHeartRate = heartRate.toPlatformRecord() as PlatformHeartRateRecord
+
+ assertPlatformRecord(platformHeartRate) {
+ assertThat(samples)
+ .comparingElementsUsing(
+ Correspondence.from<PlatformHeartRateSample, PlatformHeartRateSample>(
+ { actual, expected ->
+ actual!!.beatsPerMinute == expected!!.beatsPerMinute &&
+ actual.time == expected.time
+ },
+ "has same BPM and same time as"
+ )
+ )
+ .containsExactly(
+ PlatformHeartRateSample(55L, Instant.ofEpochMilli(1234L)),
+ PlatformHeartRateSample(57L, Instant.ofEpochMilli(5678L))
+ )
+ }
+ }
+
+ @Test
+ fun heartRateVariabilityRmssdRecord_convertToPlatform() {
+ val platformHeartRateVariabilityRmssd =
+ HeartRateVariabilityRmssdRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ heartRateVariabilityMillis = 1.0,
+ )
+ .toPlatformRecord() as PlatformHeartRateVariabilityRmssdRecord
+
+ assertPlatformRecord(platformHeartRateVariabilityRmssd) {
+ assertThat(heartRateVariabilityMillis).isEqualTo(1.0)
+ }
+ }
+
+ @Test
+ fun heightRecord_convertToPlatform() {
+ val platformHeight =
+ HeightRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ height = Length.meters(1.8),
+ )
+ .toPlatformRecord() as PlatformHeightRecord
+
+ assertPlatformRecord(platformHeight) {
+ assertThat(height).isEqualTo(PlatformLength.fromMeters(1.8))
+ }
+ }
+
+ @Test
+ fun hydrationRecord_convertToPlatform() {
+ val platformHydration =
+ HydrationRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ volume = Volume.liters(90.0),
+ )
+ .toPlatformRecord() as PlatformHydrationRecord
+
+ assertPlatformRecord(platformHydration) {
+ assertThat(volume).isEqualTo(PlatformVolume.fromLiters(90.0))
+ }
+ }
+
+ @Test
+ fun intermenstrualBleedingRecord_convertToPlatform() {
+ val platformIntermenstrualBleeding =
+ IntermenstrualBleedingRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ )
+ .toPlatformRecord() as PlatformIntermenstrualBleedingRecord
+
+ assertPlatformRecord(platformIntermenstrualBleeding)
+ }
+
+ @Test
+ fun leanBodyMassRecord_convertToPlatform() {
+ val platformLeanBodyMass =
+ LeanBodyMassRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ mass = Mass.grams(21.3),
+ )
+ .toPlatformRecord() as PlatformLeanBodyMassRecord
+
+ assertPlatformRecord(platformLeanBodyMass) {
+ assertThat(mass).isEqualTo(PlatformMass.fromGrams(21.3))
+ }
+ }
+
+ @Test
+ fun menstruationFlowRecord_convertToPlatform() {
+ val platformMenstruationFlow =
+ MenstruationFlowRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ flow = MenstruationFlowRecord.FLOW_MEDIUM,
+ )
+ .toPlatformRecord() as PlatformMenstruationFlowRecord
+
+ assertPlatformRecord(platformMenstruationFlow) {
+ assertThat(flow).isEqualTo(PlatformMenstruationFlowType.FLOW_MEDIUM)
+ }
+ }
+
+ @Test
+ fun menstruationPeriodRecord_convertToPlatform() {
+ val platformMenstruationPeriod =
+ MenstruationPeriodRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA
+ )
+ .toPlatformRecord() as PlatformMenstruationPeriodRecord
+
+ assertPlatformRecord(platformMenstruationPeriod)
+ }
+
+ @Test
+ fun nutritionRecord_convertToPlatform() {
+ val nutrition =
+ NutritionRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ calcium = Mass.grams(15.0),
+ caffeine = Mass.grams(20.0),
+ chloride = Mass.grams(25.0),
+ cholesterol = Mass.grams(30.0),
+ chromium = Mass.grams(35.0),
+ copper = Mass.grams(40.0),
+ molybdenum = Mass.grams(45.0),
+ monounsaturatedFat = Mass.grams(50.0),
+ energy = Energy.calories(300.0)
+ )
+
+ val platformNutrition = nutrition.toPlatformRecord() as PlatformNutritionRecord
+
+ assertPlatformRecord(platformNutrition) {
+ assertThat(calcium!!.inGrams).isWithin(tolerance).of(15.0)
+ assertThat(caffeine!!.inGrams).isWithin(tolerance).of(20.0)
+ assertThat(chloride!!.inGrams).isWithin(tolerance).of(25.0)
+ assertThat(cholesterol!!.inGrams).isWithin(tolerance).of(30.0)
+ assertThat(chromium!!.inGrams).isWithin(tolerance).of(35.0)
+ assertThat(copper!!.inGrams).isWithin(tolerance).of(40.0)
+ assertThat(molybdenum!!.inGrams).isWithin(tolerance).of(45.0)
+ assertThat(monounsaturatedFat!!.inGrams).isWithin(tolerance).of(50.0)
+ assertThat(energy!!.inCalories).isWithin(tolerance).of(300.0)
+ }
+ }
+
+ @Test
+ fun ovulationTestRecord_convertToPlatform() {
+ val platformOvulationTest =
+ OvulationTestRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ result = OvulationTestRecord.RESULT_POSITIVE,
+ )
+ .toPlatformRecord() as PlatformOvulationTestRecord
+
+ assertPlatformRecord(platformOvulationTest) {
+ assertThat(result).isEqualTo(PlatformOvulationTestResult.RESULT_POSITIVE)
+ }
+ }
+
+ @Test
+ fun oxygenSaturationRecord_convertToPlatform() {
+ val platformOxygenSaturation =
+ OxygenSaturationRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ percentage = Percentage(15.0),
+ )
+ .toPlatformRecord() as PlatformOxygenSaturationRecord
+
+ assertPlatformRecord(platformOxygenSaturation) {
+ assertThat(percentage).isEqualTo(PlatformPercentage.fromValue(15.0))
+ }
+ }
+
+ @Test
+ fun powerRecord_convertToPlatform() {
+ val platformPowerRecord =
+ PowerRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ samples = listOf(PowerRecord.Sample(START_TIME, Power.watts(300.0))),
+ )
+ .toPlatformRecord() as PlatformPowerRecord
+
+ assertPlatformRecord(platformPowerRecord) {
+ assertThat(samples)
+ .containsExactly(
+ PlatformPowerRecordSample(PlatformPower.fromWatts(300.0), START_TIME)
+ )
+ }
+ }
+
+ @Test
+ fun respiratoryRateRecord_convertToPlatform() {
+ val platformRespiratoryRate =
+ RespiratoryRateRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ rate = 12.0,
+ )
+ .toPlatformRecord() as PlatformRespiratoryRateRecord
+
+ assertPlatformRecord(platformRespiratoryRate) { assertThat(rate).isEqualTo(12.0) }
+ }
+
+ @Test
+ fun restingHeartRateRecord_convertToPlatform() {
+ val platformRestingHeartRate =
+ RestingHeartRateRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ beatsPerMinute = 57L,
+ )
+ .toPlatformRecord() as PlatformRestingHeartRateRecord
+
+ assertPlatformRecord(platformRestingHeartRate) { assertThat(beatsPerMinute).isEqualTo(57L) }
+ }
+
+ @Test
+ fun sexualActivityRecord_convertToPlatform() {
+ val platformSexualActivity =
+ SexualActivityRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ protectionUsed = SexualActivityRecord.PROTECTION_USED_PROTECTED,
+ )
+ .toPlatformRecord() as PlatformSexualActivityRecord
+
+ assertPlatformRecord(platformSexualActivity) {
+ assertThat(protectionUsed)
+ .isEqualTo(PlatformSexualActivityProtectionUsed.PROTECTION_USED_PROTECTED)
+ }
+ }
+
+ @Test
+ fun sleepSessionRecord_convertToPlatform() {
+ val platformSleepSession =
+ SleepSessionRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ title = "Night night",
+ notes = "Many dreams",
+ stages =
+ listOf(
+ SleepSessionRecord.Stage(
+ START_TIME,
+ START_TIME.plusMillis(40),
+ SleepSessionRecord.STAGE_TYPE_DEEP
+ )
+ )
+ )
+ .toPlatformRecord() as PlatformSleepSessionRecord
+
+ assertPlatformRecord(platformSleepSession) {
+ assertThat(title).isEqualTo("Night night")
+ assertThat(notes).isEqualTo("Many dreams")
+ assertThat(stages)
+ .containsExactly(
+ PlatformSleepSessionStage(
+ START_TIME,
+ START_TIME.plusMillis(40),
+ PlatformSleepStageType.STAGE_TYPE_SLEEPING_DEEP
+ )
+ )
+ }
+ }
+
+ @Test
+ fun speedRecord_convertToPlatform() {
+ val platformSpeed =
+ SpeedRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ samples = listOf(SpeedRecord.Sample(END_TIME, Velocity.metersPerSecond(3.0))),
+ )
+ .toPlatformRecord() as PlatformSpeedRecord
+
+ assertPlatformRecord(platformSpeed) {
+ assertThat(samples)
+ .comparingElementsUsing(
+ Correspondence.from<PlatformSpeedSample, PlatformSpeedSample>(
+ { actual, expected ->
+ actual!!.speed.inMetersPerSecond ==
+ expected!!.speed.inMetersPerSecond && actual.time == expected.time
+ },
+ "has same speed and same time as"
+ )
+ )
+ .containsExactly(
+ PlatformSpeedSample(PlatformVelocity.fromMetersPerSecond(3.0), END_TIME)
+ )
+ }
+ }
+
+ @Test
+ fun stepsRecord_convertToPlatform() {
+ val platformSteps =
+ StepsRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ count = 10,
+ )
+ .toPlatformRecord() as PlatformStepsRecord
+
+ assertPlatformRecord(platformSteps) { assertThat(count).isEqualTo(10) }
+ }
+
+ @Test
+ fun stepsCadenceRecord_convertToPlatform() {
+ val platformStepsCadence =
+ StepsCadenceRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ samples = listOf(StepsCadenceRecord.Sample(END_TIME, 99.0)),
+ )
+ .toPlatformRecord() as PlatformStepsCadenceRecord
+
+ assertPlatformRecord(platformStepsCadence) {
+ assertThat(samples)
+ .comparingElementsUsing(
+ Correspondence.from<PlatformStepsCadenceSample, PlatformStepsCadenceSample>(
+ { actual, expected ->
+ actual!!.rate == expected!!.rate && actual.time == expected.time
+ },
+ "has same rate and same time as"
+ )
+ )
+ .containsExactly(PlatformStepsCadenceSample(99.0, END_TIME))
+ }
+ }
+
+ @Test
+ fun totalCaloriesBurnedRecord_convertToPlatform() {
+ val platformTotalCaloriesBurned =
+ TotalCaloriesBurnedRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ energy = Energy.calories(100.0),
+ )
+ .toPlatformRecord() as PlatformTotalCaloriesBurnedRecord
+
+ assertPlatformRecord(platformTotalCaloriesBurned) {
+ assertThat(energy).isEqualTo(PlatformEnergy.fromCalories(100.0))
+ }
+ }
+
+ @Test
+ fun vo2MaxRecord_convertToPlatform() {
+ val platformVo2Max =
+ Vo2MaxRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ vo2MillilitersPerMinuteKilogram = 5.0,
+ measurementMethod = Vo2MaxRecord.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST
+ )
+ .toPlatformRecord() as PlatformVo2MaxRecord
+
+ assertPlatformRecord(platformVo2Max) {
+ assertThat(vo2MillilitersPerMinuteKilogram).isEqualTo(5.0)
+ assertThat(measurementMethod)
+ .isEqualTo(
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST
+ )
+ }
+ }
+
+ @Test
+ fun weightRecord_convertToPlatform() {
+ val platformWeight =
+ WeightRecord(
+ time = TIME,
+ zoneOffset = ZONE_OFFSET,
+ metadata = METADATA,
+ weight = Mass.grams(100.0),
+ )
+ .toPlatformRecord() as PlatformWeightRecord
+
+ assertPlatformRecord(platformWeight) {
+ assertThat(weight).isEqualTo(PlatformMass.fromGrams(100.0))
+ }
+ }
+
+ @Test
+ fun wheelChairPushesRecord_convertToPlatform() {
+ val platformWheelchairPushes =
+ WheelchairPushesRecord(
+ startTime = START_TIME,
+ startZoneOffset = START_ZONE_OFFSET,
+ endTime = END_TIME,
+ endZoneOffset = END_ZONE_OFFSET,
+ metadata = METADATA,
+ count = 10,
+ )
+ .toPlatformRecord() as PlatformWheelchairPushesRecord
+
+ assertPlatformRecord(platformWheelchairPushes) { assertThat(count).isEqualTo(10) }
+ }
+
+ @Test
+ fun activeCaloriesBurnedRecord_convertToSdk() {
+ val sdkActiveCaloriesBurned =
+ PlatformActiveCaloriesBurnedRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformEnergy.fromCalories(300.0)
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as ActiveCaloriesBurnedRecord
+
+ assertSdkRecord(sdkActiveCaloriesBurned) {
+ assertThat(energy).isEqualTo(Energy.calories(300.0))
+ }
+ }
+
+ @Test
+ fun basalBodyTemperatureRecord_convertToSdk() {
+ val sdkBasalBodyTemperature =
+ PlatformBasalBodyTemperatureRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_RECTUM,
+ PlatformTemperature.fromCelsius(37.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BasalBodyTemperatureRecord
+
+ assertSdkRecord(sdkBasalBodyTemperature) {
+ assertThat(measurementLocation)
+ .isEqualTo(BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_RECTUM)
+ assertThat(temperature).isEqualTo(Temperature.celsius(37.0))
+ }
+ }
+
+ @Test
+ fun basalMetabolicRateRecord_convertToSdk() {
+ val sdkBasalMetabolicRate =
+ PlatformBasalMetabolicRateRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformPower.fromWatts(100.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BasalMetabolicRateRecord
+
+ assertSdkRecord(sdkBasalMetabolicRate) {
+ assertThat(basalMetabolicRate).isEqualTo(Power.watts(100.0))
+ }
+ }
+
+ @Test
+ fun bloodGlucoseRecord_convertToSdk() {
+ val sdkBloodGlucose =
+ PlatformBloodGlucoseRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_TEARS,
+ PlatformBloodGlucose.fromMillimolesPerLiter(10.2),
+ PlatformBloodGlucoseRelationToMealType.RELATION_TO_MEAL_FASTING,
+ PlatformMealType.MEAL_TYPE_SNACK
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BloodGlucoseRecord
+
+ assertSdkRecord(sdkBloodGlucose) {
+ assertThat(level).isEqualTo(BloodGlucose.millimolesPerLiter(10.2))
+ assertThat(specimenSource).isEqualTo(BloodGlucoseRecord.SPECIMEN_SOURCE_TEARS)
+ assertThat(mealType).isEqualTo(MealType.MEAL_TYPE_SNACK)
+ assertThat(relationToMeal).isEqualTo(BloodGlucoseRecord.RELATION_TO_MEAL_FASTING)
+ }
+ }
+
+ @Test
+ fun bloodPressureRecord_convertToSdk() {
+ val sdkBloodPressure =
+ PlatformBloodPressureRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformBloodPressureMeasurementLocation
+ .BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST,
+ PlatformPressure.fromMillimetersOfMercury(20.0),
+ PlatformPressure.fromMillimetersOfMercury(15.0),
+ PlatformBloodPressureBodyPosition.BODY_POSITION_STANDING_UP
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BloodPressureRecord
+
+ assertSdkRecord(sdkBloodPressure) {
+ assertThat(measurementLocation)
+ .isEqualTo(BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST)
+ assertThat(systolic).isEqualTo(Pressure.millimetersOfMercury(20.0))
+ assertThat(diastolic).isEqualTo(Pressure.millimetersOfMercury(15.0))
+ assertThat(bodyPosition).isEqualTo(BloodPressureRecord.BODY_POSITION_STANDING_UP)
+ }
+ }
+
+ @Test
+ fun bodyFatRecord_convertToSdk() {
+ val sdkBodyFat =
+ PlatformBodyFatRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformPercentage.fromValue(18.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BodyFatRecord
+
+ assertSdkRecord(sdkBodyFat) { assertThat(percentage).isEqualTo(Percentage(18.0)) }
+ }
+
+ @Test
+ fun bodyTemperatureRecord_convertToSdk() {
+ val sdkBodyTemperature =
+ PlatformBodyTemperatureRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_WRIST,
+ PlatformTemperature.fromCelsius(27.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BodyTemperatureRecord
+
+ assertSdkRecord(sdkBodyTemperature) {
+ assertThat(measurementLocation)
+ .isEqualTo(BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_WRIST)
+ assertThat(temperature).isEqualTo(Temperature.celsius(27.0))
+ }
+ }
+
+ @Test
+ fun bodyWaterMassRecord_convertToSdk() {
+ val sdkBodyWaterMass =
+ PlatformBodyWaterMassRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformMass.fromGrams(12.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BodyWaterMassRecord
+
+ assertSdkRecord(sdkBodyWaterMass) { assertThat(mass).isEqualTo(Mass.grams(12.0)) }
+ }
+
+ @Test
+ fun boneMassRecord_convertToSdk() {
+ val sdkBoneMass =
+ PlatformBoneMassRecordBuilder(PLATFORM_METADATA, TIME, PlatformMass.fromGrams(73.0))
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as BoneMassRecord
+
+ assertSdkRecord(sdkBoneMass) { assertThat(mass).isEqualTo(Mass.grams(73.0)) }
+ }
+
+ @Test
+ fun cervicalMucusRecord_convertToSdk() {
+ val sdkCervicalMucus =
+ PlatformCervicalMucusRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformCervicalMucusSensation.SENSATION_HEAVY,
+ PlatformCervicalMucusAppearance.APPEARANCE_DRY
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as CervicalMucusRecord
+
+ assertSdkRecord(sdkCervicalMucus) {
+ assertThat(sensation).isEqualTo(CervicalMucusRecord.SENSATION_HEAVY)
+ assertThat(appearance).isEqualTo(CervicalMucusRecord.APPEARANCE_DRY)
+ }
+ }
+
+ @Test
+ fun cyclingPedalingCadenceRecord_convertToSdk() {
+ val sdkCyclingPedalingCadence =
+ PlatformCyclingPedalingCadenceRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ listOf(PlatformCyclingPedalingCadenceSample(23.0, END_TIME))
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as CyclingPedalingCadenceRecord
+
+ assertSdkRecord(sdkCyclingPedalingCadence) {
+ assertThat(samples).containsExactly(CyclingPedalingCadenceRecord.Sample(END_TIME, 23.0))
+ }
+ }
+
+ @Test
+ fun distanceRecord_convertToSdk() {
+ val sdkDistance =
+ PlatformDistanceRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformLength.fromMeters(500.0)
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as DistanceRecord
+
+ assertSdkRecord(sdkDistance) { assertThat(distance).isEqualTo(Length.meters(500.0)) }
+ }
+
+ @Test
+ fun elevationGainedRecord_convertToSdk() {
+ val sdkElevationGained =
+ PlatformElevationGainedRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformLength.fromMeters(10.0)
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as ElevationGainedRecord
+
+ assertSdkRecord(sdkElevationGained) { assertThat(elevation).isEqualTo(Length.meters(10.0)) }
+ }
+
+ @Test
+ fun exerciseSessionRecord_convertToSdk() {
+ val sdkExerciseSession =
+ PlatformExerciseSessionRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformExerciseSessionType
+ .EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
+ )
+ .setTitle("Training")
+ .setNotes("Improve jump serve")
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .setLaps(
+ listOf(
+ PlatformExerciseLapBuilder(
+ START_TIME.plusMillis(6),
+ START_TIME.plusMillis(10)
+ )
+ .setLength(PlatformLength.fromMeters(1.0))
+ .build(),
+ PlatformExerciseLapBuilder(
+ START_TIME.plusMillis(11),
+ START_TIME.plusMillis(15)
+ )
+ .setLength(PlatformLength.fromMeters(1.5))
+ .build()
+ )
+ )
+ .setSegments(
+ listOf(
+ PlatformExerciseSegmentBuilder(
+ START_TIME.plusMillis(1),
+ START_TIME.plusMillis(10),
+ PlatformExerciseSegmentType
+ .EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS
+ )
+ .setRepetitionsCount(10)
+ .build()
+ )
+ )
+ .setRoute(
+ PlatformExerciseRoute(
+ listOf(
+ PlatformExerciseRouteLocationBuilder(START_TIME, 23.4, -23.4)
+ .setAltitude(PlatformLength.fromMeters(10.0))
+ .setHorizontalAccuracy(PlatformLength.fromMeters(2.0))
+ .setVerticalAccuracy(PlatformLength.fromMeters(3.0))
+ .build()
+ )
+ )
+ )
+ .build()
+ .toSdkRecord() as ExerciseSessionRecord
+
+ assertSdkRecord(sdkExerciseSession) {
+ assertThat(title).isEqualTo("Training")
+ assertThat(notes).isEqualTo("Improve jump serve")
+ assertThat(exerciseType)
+ .isEqualTo(ExerciseSessionRecord.EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING)
+ assertThat(laps)
+ .containsExactly(
+ ExerciseLap(
+ START_TIME.plusMillis(6),
+ START_TIME.plusMillis(10),
+ Length.meters(1.0)
+ ),
+ ExerciseLap(
+ START_TIME.plusMillis(11),
+ START_TIME.plusMillis(15),
+ Length.meters(1.5)
+ )
+ )
+ assertThat(segments)
+ .containsExactly(
+ ExerciseSegment(
+ START_TIME.plusMillis(1),
+ START_TIME.plusMillis(10),
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+ 10
+ )
+ )
+ assertThat(route)
+ .isEqualTo(
+ ExerciseRoute(
+ listOf(
+ ExerciseRoute.Location(
+ time = START_TIME,
+ latitude = 23.4,
+ longitude = -23.4,
+ altitude = Length.meters(10.0),
+ horizontalAccuracy = Length.meters(2.0),
+ verticalAccuracy = Length.meters(3.0)
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun floorsClimbedRecord_convertToSdk() {
+ val sdkFloorsClimbed =
+ PlatformFloorsClimbedRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME, 10.0)
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as FloorsClimbedRecord
+
+ assertSdkRecord(sdkFloorsClimbed) { assertThat(floors).isEqualTo(10.0) }
+ }
+
+ @Test
+ fun heartRateRecord_convertToSdk() {
+ val sdkHeartRate =
+ PlatformHeartRateRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ listOf(PlatformHeartRateSample(83, START_TIME))
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as HeartRateRecord
+
+ assertSdkRecord(sdkHeartRate) {
+ assertThat(samples).containsExactly(HeartRateRecord.Sample(START_TIME, 83))
+ }
+ }
+
+ @Test
+ fun heartRateVariabilityRmssdRecord_convertToSdk() {
+ val sdkHeartRateVariabilityRmssd =
+ PlatformHeartRateVariabilityRmssdRecordBuilder(PLATFORM_METADATA, TIME, 1.6)
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as HeartRateVariabilityRmssdRecord
+
+ assertSdkRecord(sdkHeartRateVariabilityRmssd) {
+ assertThat(heartRateVariabilityMillis).isEqualTo(1.6)
+ }
+ }
+
+ @Test
+ fun heightRecord_convertToSdk() {
+ val sdkHeight =
+ PlatformHeightRecordBuilder(PLATFORM_METADATA, TIME, PlatformLength.fromMeters(1.7))
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as HeightRecord
+
+ assertSdkRecord(sdkHeight) { assertThat(height).isEqualTo(Length.meters(1.7)) }
+ }
+
+ @Test
+ fun hydrationRecord_convertToSdk() {
+ val sdkHydration =
+ PlatformHydrationRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformVolume.fromLiters(90.0)
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as HydrationRecord
+
+ assertSdkRecord(sdkHydration) { assertThat(volume).isEqualTo(Volume.liters(90.0)) }
+ }
+
+ @Test
+ fun intermenstrualBleedingRecord_convertToSdk() {
+ val sdkIntermenstrualBleeding =
+ PlatformIntermenstrualBleedingRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as IntermenstrualBleedingRecord
+
+ assertSdkRecord(sdkIntermenstrualBleeding)
+ }
+
+ @Test
+ fun leanBodyMassRecord_convertToSdk() {
+ val sdkLeanBodyMass =
+ PlatformLeanBodyMassRecordBuilder(PLATFORM_METADATA, TIME, PlatformMass.fromGrams(9.0))
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as LeanBodyMassRecord
+
+ assertSdkRecord(sdkLeanBodyMass) { assertThat(mass).isEqualTo(Mass.grams(9.0)) }
+ }
+
+ @Test
+ fun menstruationFlowRecord_convertToSdk() {
+ val sdkMenstruationFlow =
+ PlatformMenstruationFlowRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformMenstruationFlowType.FLOW_MEDIUM
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as MenstruationFlowRecord
+
+ assertSdkRecord(sdkMenstruationFlow) {
+ assertThat(flow).isEqualTo(MenstruationFlowRecord.FLOW_MEDIUM)
+ }
+ }
+
+ @Test
+ fun menstruationPeriodRecord_convertToSdk() {
+ val sdkMenstruationPeriod =
+ PlatformMenstruationPeriodRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME)
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as MenstruationPeriodRecord
+
+ assertSdkRecord(sdkMenstruationPeriod)
+ }
+
+ @Test
+ fun nutritionRecord_convertToSdk() {
+ val sdkNutrition =
+ PlatformNutritionRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME)
+ .setMealName("Cheat meal")
+ .setMealType(PlatformMealType.MEAL_TYPE_DINNER)
+ .setChromium(PlatformMass.fromGrams(0.01))
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as NutritionRecord
+
+ assertSdkRecord(sdkNutrition) {
+ assertThat(name).isEqualTo("Cheat meal")
+ assertThat(mealType).isEqualTo(MealType.MEAL_TYPE_DINNER)
+ assertThat(chromium).isEqualTo(Mass.grams(0.01))
+ }
+ }
+
+ @Test
+ fun ovulationTestRecord_convertToSdk() {
+ val sdkOvulationTest =
+ PlatformOvulationTestRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformOvulationTestResult.RESULT_NEGATIVE
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as OvulationTestRecord
+
+ assertSdkRecord(sdkOvulationTest) {
+ assertThat(result).isEqualTo(OvulationTestRecord.RESULT_NEGATIVE)
+ }
+ }
+
+ @Test
+ fun oxygenSaturationRecord_convertToSdk() {
+ val sdkOxygenSaturation =
+ PlatformOxygenSaturationRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformPercentage.fromValue(21.0)
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as OxygenSaturationRecord
+
+ assertSdkRecord(sdkOxygenSaturation) { assertThat(percentage).isEqualTo(Percentage(21.0)) }
+ }
+
+ @Test
+ fun powerRecord_convertToSdk() {
+ val sdkPower =
+ PlatformPowerRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ listOf(PlatformPowerRecordSample(PlatformPower.fromWatts(300.0), START_TIME))
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as PowerRecord
+
+ assertSdkRecord(sdkPower) {
+ assertThat(samples).containsExactly(PowerRecord.Sample(START_TIME, Power.watts(300.0)))
+ }
+ }
+
+ @Test
+ fun respiratoryRateRecord_convertToSdk() {
+ val sdkRespiratoryRate =
+ PlatformRespiratoryRateRecordBuilder(PLATFORM_METADATA, TIME, 12.0)
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as RespiratoryRateRecord
+
+ assertSdkRecord(sdkRespiratoryRate) { assertThat(rate).isEqualTo(12.0) }
+ }
+
+ @Test
+ fun restingHeartRateRecord_convertToSdk() {
+ val sdkRestingHeartRate =
+ PlatformRestingHeartRateRecordBuilder(PLATFORM_METADATA, TIME, 37)
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as RestingHeartRateRecord
+
+ assertSdkRecord(sdkRestingHeartRate) { assertThat(beatsPerMinute).isEqualTo(37) }
+ }
+
+ @Test
+ fun sexualActivityRecord_convertToSdk() {
+ val sdkSexualActivity =
+ PlatformSexualActivityRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformSexualActivityProtectionUsed.PROTECTION_USED_PROTECTED
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as SexualActivityRecord
+
+ assertSdkRecord(sdkSexualActivity) {
+ assertThat(protectionUsed).isEqualTo(SexualActivityRecord.PROTECTION_USED_PROTECTED)
+ }
+ }
+
+ @Test
+ fun sleepSessionRecord_convertToSdk() {
+ val sdkSleepSession =
+ PlatformSleepSessionRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME)
+ .setTitle("nap")
+ .setNotes("Afternoon reset")
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .setStages(
+ listOf(
+ PlatformSleepSessionStage(
+ START_TIME,
+ START_TIME.plusMillis(1),
+ PlatformSleepStageType.STAGE_TYPE_AWAKE
+ ),
+ PlatformSleepSessionStage(
+ END_TIME.minusMillis(1),
+ END_TIME,
+ PlatformSleepStageType.STAGE_TYPE_SLEEPING
+ )
+ )
+ )
+ .build()
+ .toSdkRecord() as SleepSessionRecord
+
+ assertSdkRecord(sdkSleepSession) {
+ assertThat(title).isEqualTo("nap")
+ assertThat(notes).isEqualTo("Afternoon reset")
+ assertThat(stages)
+ .containsExactly(
+ SleepSessionRecord.Stage(
+ START_TIME,
+ START_TIME.plusMillis(1),
+ SleepSessionRecord.STAGE_TYPE_AWAKE
+ ),
+ SleepSessionRecord.Stage(
+ END_TIME.minusMillis(1),
+ END_TIME,
+ SleepSessionRecord.STAGE_TYPE_SLEEPING
+ )
+ )
+ }
+ }
+
+ @Test
+ fun speedRecord_convertToSdk() {
+ val sdkSpeed =
+ PlatformSpeedRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ listOf(
+ PlatformSpeedSample(PlatformVelocity.fromMetersPerSecond(99.0), END_TIME)
+ )
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as SpeedRecord
+
+ assertSdkRecord(sdkSpeed) {
+ assertThat(samples)
+ .containsExactly(SpeedRecord.Sample(END_TIME, Velocity.metersPerSecond(99.0)))
+ }
+ }
+
+ @Test
+ fun stepsCadenceRecord_convertToSdk() {
+ val sdkStepsCadence =
+ PlatformStepsCadenceRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ listOf(PlatformStepsCadenceSample(10.0, END_TIME))
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as StepsCadenceRecord
+
+ assertSdkRecord(sdkStepsCadence) {
+ assertThat(samples).containsExactly(StepsCadenceRecord.Sample(END_TIME, 10.0))
+ }
+ }
+
+ @Test
+ fun stepsRecord_convertToSdk() {
+ val sdkSteps =
+ PlatformStepsRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME, 10)
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as StepsRecord
+
+ assertSdkRecord(sdkSteps) { assertThat(count).isEqualTo(10) }
+ }
+
+ @Test
+ fun totalCaloriesBurnedRecord_convertToSdk() {
+ val sdkTotalCaloriesBurned =
+ PlatformTotalCaloriesBurnedRecordBuilder(
+ PLATFORM_METADATA,
+ START_TIME,
+ END_TIME,
+ PlatformEnergy.fromCalories(333.0)
+ )
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as TotalCaloriesBurnedRecord
+
+ assertSdkRecord(sdkTotalCaloriesBurned) {
+ assertThat(energy).isEqualTo(Energy.calories(333.0))
+ }
+ }
+
+ @Test
+ fun vo2MaxRecord_convertToSdk() {
+ val sdkVo2Max =
+ PlatformVo2MaxRecordBuilder(
+ PLATFORM_METADATA,
+ TIME,
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST,
+ 13.0
+ )
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as Vo2MaxRecord
+
+ assertSdkRecord(sdkVo2Max) {
+ assertThat(measurementMethod)
+ .isEqualTo(Vo2MaxRecord.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST)
+ assertThat(vo2MillilitersPerMinuteKilogram).isEqualTo(13.0)
+ }
+ }
+
+ @Test
+ fun weightRecord_convertToSdk() {
+ val sdkWeight =
+ PlatformWeightRecordBuilder(PLATFORM_METADATA, TIME, PlatformMass.fromGrams(63.0))
+ .setZoneOffset(ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as WeightRecord
+
+ assertSdkRecord(sdkWeight) { assertThat(weight).isEqualTo(Mass.grams(63.0)) }
+ }
+
+ @Test
+ fun wheelChairPushesRecord_convertToSdk() {
+ val sdkWheelchairPushes =
+ PlatformWheelchairPushesRecordBuilder(PLATFORM_METADATA, START_TIME, END_TIME, 18)
+ .setStartZoneOffset(START_ZONE_OFFSET)
+ .setEndZoneOffset(END_ZONE_OFFSET)
+ .build()
+ .toSdkRecord() as WheelchairPushesRecord
+
+ assertSdkRecord(sdkWheelchairPushes) { assertThat(count).isEqualTo(18) }
+ }
+
+ private fun <T : PlatformIntervalRecord> assertPlatformRecord(platformRecord: T) {
+ assertPlatformRecord(platformRecord) {}
+ }
+
+ private fun <T : PlatformIntervalRecord> assertPlatformRecord(
+ platformRecord: T,
+ typeSpecificAssertions: T.() -> Unit
+ ) {
+ assertThat(platformRecord.startTime).isEqualTo(START_TIME)
+ assertThat(platformRecord.startZoneOffset).isEqualTo(START_ZONE_OFFSET)
+ assertThat(platformRecord.endTime).isEqualTo(END_TIME)
+ assertThat(platformRecord.endZoneOffset).isEqualTo(END_ZONE_OFFSET)
+ assertThat(platformRecord.metadata).isEqualTo(PLATFORM_METADATA)
+ platformRecord.typeSpecificAssertions()
+ }
+
+ private fun <T : PlatformInstantRecord> assertPlatformRecord(platformRecord: T) =
+ assertPlatformRecord(platformRecord) {}
+
+ private fun <T : PlatformInstantRecord> assertPlatformRecord(
+ platformRecord: T,
+ typeSpecificAssertions: T.() -> Unit
+ ) {
+ assertThat(platformRecord.time).isEqualTo(TIME)
+ assertThat(platformRecord.zoneOffset).isEqualTo(ZONE_OFFSET)
+ assertThat(platformRecord.metadata).isEqualTo(PLATFORM_METADATA)
+ platformRecord.typeSpecificAssertions()
+ }
+
+ private fun <T : IntervalRecord> assertSdkRecord(sdkRecord: T) = assertSdkRecord(sdkRecord) {}
+
+ private fun <T : IntervalRecord> assertSdkRecord(
+ sdkRecord: T,
+ typeSpecificAssertions: T.() -> Unit
+ ) {
+ assertThat(sdkRecord.startTime).isEqualTo(START_TIME)
+ assertThat(sdkRecord.startZoneOffset).isEqualTo(START_ZONE_OFFSET)
+ assertThat(sdkRecord.endTime).isEqualTo(END_TIME)
+ assertThat(sdkRecord.endZoneOffset).isEqualTo(END_ZONE_OFFSET)
+ assertThat(sdkRecord.metadata.id).isEqualTo(METADATA.id)
+ assertThat(sdkRecord.metadata.dataOrigin).isEqualTo(METADATA.dataOrigin)
+ sdkRecord.typeSpecificAssertions()
+ }
+
+ private fun <T : InstantaneousRecord> assertSdkRecord(sdkRecord: T) =
+ assertSdkRecord(sdkRecord) {}
+
+ private fun <T : InstantaneousRecord> assertSdkRecord(
+ sdkRecord: T,
+ typeSpecificAssertions: T.() -> Unit
+ ) {
+ assertThat(sdkRecord.time).isEqualTo(TIME)
+ assertThat(sdkRecord.zoneOffset).isEqualTo(ZONE_OFFSET)
+ assertThat(sdkRecord.metadata.id).isEqualTo(METADATA.id)
+ assertThat(sdkRecord.metadata.dataOrigin).isEqualTo(METADATA.dataOrigin)
+ sdkRecord.typeSpecificAssertions()
+ }
+
+ private companion object {
+ val TIME: Instant = Instant.ofEpochMilli(1235L)
+ val ZONE_OFFSET: ZoneOffset = ZoneOffset.UTC
+
+ val START_TIME: Instant = Instant.ofEpochMilli(1234L)
+ val END_TIME: Instant = Instant.ofEpochMilli(56780L)
+ val START_ZONE_OFFSET: ZoneOffset = ZoneOffset.UTC
+ val END_ZONE_OFFSET: ZoneOffset = ZoneOffset.ofHours(2)
+
+ val METADATA = Metadata(id = "someId", dataOrigin = DataOrigin("somePackage"))
+
+ val PLATFORM_METADATA =
+ PlatformMetadataBuilder()
+ .setId("someId")
+ .setDataOrigin(PlatformDataOriginBuilder().setPackageName("somePackage").build())
+ .build()
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RequestConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RequestConvertersTest.kt
new file mode 100644
index 0000000..4dcd780
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RequestConvertersTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.annotation.TargetApi
+import android.health.connect.LocalTimeRangeFilter
+import android.health.connect.TimeInstantRangeFilter
+import android.health.connect.datatypes.DataOrigin as PlatformDataOrigin
+import android.health.connect.datatypes.HeartRateRecord as PlatformHeartRateRecord
+import android.health.connect.datatypes.NutritionRecord as PlatformNutritionRecord
+import android.health.connect.datatypes.StepsRecord as PlatformStepsRecord
+import android.health.connect.datatypes.WheelchairPushesRecord as PlatformWheelchairPushesRecord
+import android.os.Build
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.request.AggregateGroupByDurationRequest
+import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ChangesTokenRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
+import androidx.health.connect.client.time.TimeRangeFilter
+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 java.time.Duration
+import java.time.Instant
+import java.time.LocalDateTime
+import java.time.Month
+import java.time.Period
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@SmallTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class RequestConvertersTest {
+
+ @Test
+ fun readRecordsRequest_fromSdkToPlatform() {
+ val sdkRequest =
+ ReadRecordsRequest(
+ StepsRecord::class,
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L)),
+ setOf(DataOrigin("package1"), DataOrigin("package2"))
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ assertThat(recordType).isAssignableTo(PlatformStepsRecord::class.java)
+ assertThat(isAscending).isTrue() // Default Order
+ assertThat(dataOrigins)
+ .containsExactly(
+ PlatformDataOrigin.Builder().setPackageName("package1").build(),
+ PlatformDataOrigin.Builder().setPackageName("package2").build()
+ )
+ }
+ }
+
+ @Test
+ fun readRecordsRequest_fromSdkToPlatform_ascendingOrderIgnoredWhenPageTokenIsSet() {
+ val sdkRequest =
+ ReadRecordsRequest(
+ StepsRecord::class,
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L)),
+ setOf(DataOrigin("package1"), DataOrigin("package2")),
+ ascendingOrder = false,
+ pageToken = "123"
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ assertThat(recordType).isAssignableTo(PlatformStepsRecord::class.java)
+ assertThat(pageToken).isEqualTo(123)
+ assertThat(dataOrigins)
+ .containsExactly(
+ PlatformDataOrigin.Builder().setPackageName("package1").build(),
+ PlatformDataOrigin.Builder().setPackageName("package2").build()
+ )
+ }
+ }
+
+ @Test
+ fun timeRangeFilter_instant_fromSdkToPlatform() {
+ val sdkFilter =
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L))
+
+ with(sdkFilter.toPlatformTimeRangeFilter() as TimeInstantRangeFilter) {
+ assertThat(endTime).isEqualTo(Instant.ofEpochMilli(456L))
+ }
+ }
+
+ @Test
+ fun timeRangeFilter_localDateTime_fromSdkToPlatform() {
+ val sdkFilter = TimeRangeFilter.before(LocalDateTime.of(2023, Month.MARCH, 10, 17, 30))
+
+ with(sdkFilter.toPlatformTimeRangeFilter() as LocalTimeRangeFilter) {
+ assertThat(endTime).isEqualTo(LocalDateTime.of(2023, Month.MARCH, 10, 17, 30))
+ }
+ }
+
+ @Test
+ fun timeRangeFilter_fromSdkToPlatform_none() {
+
+ val sdkFilter = TimeRangeFilter.none()
+
+ with(sdkFilter.toPlatformTimeRangeFilter() as TimeInstantRangeFilter) {
+ assertThat(startTime).isEqualTo(Instant.EPOCH)
+ }
+ }
+
+ @Test
+ fun changesTokenRequest_fromSdkToPlatform() {
+ val sdkRequest =
+ ChangesTokenRequest(
+ setOf(StepsRecord::class, HeartRateRecord::class),
+ setOf(DataOrigin("package1"), DataOrigin("package2"))
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ assertThat(recordTypes)
+ .containsExactly(
+ PlatformStepsRecord::class.java,
+ PlatformHeartRateRecord::class.java
+ )
+ assertThat(dataOriginFilters)
+ .containsExactly(
+ PlatformDataOrigin.Builder().setPackageName("package1").build(),
+ PlatformDataOrigin.Builder().setPackageName("package2").build()
+ )
+ }
+ }
+
+ @Test
+ fun aggregateRequest_fromSdkToPlatform() {
+ val sdkRequest =
+ AggregateRequest(
+ setOf(StepsRecord.COUNT_TOTAL, NutritionRecord.CAFFEINE_TOTAL),
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L)),
+ setOf(DataOrigin("package1"))
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ with(timeRangeFilter as TimeInstantRangeFilter) {
+ assertThat(startTime).isEqualTo(Instant.ofEpochMilli(123L))
+ assertThat(endTime).isEqualTo(Instant.ofEpochMilli(456L))
+ }
+ assertThat(aggregationTypes)
+ .containsExactly(
+ PlatformStepsRecord.STEPS_COUNT_TOTAL,
+ PlatformNutritionRecord.CAFFEINE_TOTAL
+ )
+ assertThat(dataOriginsFilters)
+ .containsExactly(PlatformDataOrigin.Builder().setPackageName("package1").build())
+ }
+ }
+
+ @Test
+ fun aggregateGroupByDurationRequest_fromSdkToPlatform() {
+ val sdkRequest =
+ AggregateGroupByDurationRequest(
+ setOf(NutritionRecord.ENERGY_TOTAL),
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L)),
+ Duration.ofDays(1),
+ setOf(DataOrigin("package1"), DataOrigin("package2"))
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ with(timeRangeFilter as TimeInstantRangeFilter) {
+ assertThat(startTime).isEqualTo(Instant.ofEpochMilli(123L))
+ assertThat(endTime).isEqualTo(Instant.ofEpochMilli(456L))
+ }
+ assertThat(aggregationTypes).containsExactly(PlatformNutritionRecord.ENERGY_TOTAL)
+ assertThat(dataOriginsFilters)
+ .containsExactly(
+ PlatformDataOrigin.Builder().setPackageName("package1").build(),
+ PlatformDataOrigin.Builder().setPackageName("package2").build()
+ )
+ }
+ }
+
+ @Test
+ fun aggregateGroupByPeriodRequest_fromSdkToPlatform() {
+ val sdkRequest =
+ AggregateGroupByPeriodRequest(
+ setOf(HeartRateRecord.BPM_MAX, HeartRateRecord.BPM_MIN, HeartRateRecord.BPM_AVG),
+ TimeRangeFilter.between(Instant.ofEpochMilli(123L), Instant.ofEpochMilli(456L)),
+ Period.ofDays(1),
+ setOf(DataOrigin("package1"), DataOrigin("package2"), DataOrigin("package3"))
+ )
+
+ with(sdkRequest.toPlatformRequest()) {
+ with(timeRangeFilter as TimeInstantRangeFilter) {
+ assertThat(startTime).isEqualTo(Instant.ofEpochMilli(123L))
+ assertThat(endTime).isEqualTo(Instant.ofEpochMilli(456L))
+ }
+ assertThat(aggregationTypes)
+ .containsExactly(
+ PlatformHeartRateRecord.BPM_MAX,
+ PlatformHeartRateRecord.BPM_MIN,
+ PlatformHeartRateRecord.BPM_AVG
+ )
+ assertThat(dataOriginsFilters)
+ .containsExactly(
+ PlatformDataOrigin.Builder().setPackageName("package1").build(),
+ PlatformDataOrigin.Builder().setPackageName("package2").build(),
+ PlatformDataOrigin.Builder().setPackageName("package3").build()
+ )
+ }
+ }
+
+ @Test
+ fun toAggregationType_convertFromSdkToPlatform() {
+ assertThat(WheelchairPushesRecord.COUNT_TOTAL.toAggregationType())
+ .isEqualTo(PlatformWheelchairPushesRecord.WHEEL_CHAIR_PUSHES_COUNT_TOTAL)
+ assertThat(NutritionRecord.ENERGY_TOTAL.toAggregationType())
+ .isEqualTo(PlatformNutritionRecord.ENERGY_TOTAL)
+ }
+}
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/ResponseConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/ResponseConvertersTest.kt
new file mode 100644
index 0000000..986a99b
--- /dev/null
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/ResponseConvertersTest.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.annotation.TargetApi
+import android.health.connect.datatypes.units.Energy as PlatformEnergy
+import android.health.connect.datatypes.units.Length as PlatformLength
+import android.health.connect.datatypes.units.Mass as PlatformMass
+import android.health.connect.datatypes.units.Power as PlatformPower
+import android.health.connect.datatypes.units.Volume as PlatformVolume
+import android.os.Build
+import androidx.health.connect.client.aggregate.AggregateMetric
+import androidx.health.connect.client.records.BasalMetabolicRateRecord
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.FloorsClimbedRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.HydrationRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.PowerRecord
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+@SmallTest
+@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+// Comment the SDK suppress to run on emulators lower than U.
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class ResponseConvertersTest {
+
+ private val tolerance = Correspondence.tolerance(1e-6)
+
+ @Test
+ fun getLongMetricValues_convertsValueAccurately() {
+ val metricValues =
+ getLongMetricValues(
+ mapOf(
+ HeartRateRecord.BPM_MIN as AggregateMetric<Any> to 53L,
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL as AggregateMetric<Any> to 60_000L
+ )
+ )
+ assertThat(metricValues)
+ .containsExactly(
+ HeartRateRecord.BPM_MIN.metricKey,
+ 53L,
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL.metricKey,
+ 60_000L
+ )
+ }
+
+ @Test
+ fun getLongMetricValues_ignoresNonLongMetricTypes() {
+ val metricValues =
+ getLongMetricValues(
+ mapOf(
+ NutritionRecord.ENERGY_TOTAL as AggregateMetric<Any> to
+ PlatformEnergy.fromCalories(418_400.0)
+ )
+ )
+ assertThat(metricValues).isEmpty()
+ }
+
+ @Test
+ fun getDoubleMetricValues_convertsEnergyToKilocalories() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ NutritionRecord.ENERGY_TOTAL as AggregateMetric<Any> to
+ PlatformEnergy.fromCalories(418_400.0)
+ )
+ )
+ assertThat(metricValues)
+ .comparingValuesUsing(tolerance)
+ .containsExactly(NutritionRecord.ENERGY_TOTAL.metricKey, 418.4)
+ }
+
+ @Test
+ fun getDoubleMetricValues_convertsLengthToMeters() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ DistanceRecord.DISTANCE_TOTAL as AggregateMetric<Any> to
+ PlatformLength.fromMeters(50.0)
+ )
+ )
+ assertThat(metricValues).containsExactly(DistanceRecord.DISTANCE_TOTAL.metricKey, 50.0)
+ }
+
+ @Test
+ fun getDoubleMetricValues_convertsMassToGrams() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ NutritionRecord.BIOTIN_TOTAL as AggregateMetric<Any> to
+ PlatformMass.fromGrams(88.0)
+ )
+ )
+ assertThat(metricValues).containsExactly(NutritionRecord.BIOTIN_TOTAL.metricKey, 88.0)
+ }
+
+ @Test
+ fun getDoubleMetricValues_convertsPowerToWatts() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ PowerRecord.POWER_AVG as AggregateMetric<Any> to PlatformPower.fromWatts(366.0)
+ )
+ )
+ assertThat(metricValues).containsExactly(PowerRecord.POWER_AVG.metricKey, 366.0)
+ }
+
+ @Test
+ fun getDoubleMetricValues_convertsVolumeToLiters() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ HydrationRecord.VOLUME_TOTAL as AggregateMetric<Any> to
+ PlatformVolume.fromLiters(1.5)
+ )
+ )
+ assertThat(metricValues).containsExactly(HydrationRecord.VOLUME_TOTAL.metricKey, 1.5)
+ }
+
+ @Test
+ fun getDoubleMetricValues_ignoresNonDoubleMetricTypes() {
+ val metricValues =
+ getDoubleMetricValues(mapOf(HeartRateRecord.BPM_MIN as AggregateMetric<Any> to 53L))
+ assertThat(metricValues).isEmpty()
+ }
+
+ @Test
+ fun getLongMetricValues_handlesMultipleMetrics() {
+ val metricValues =
+ getLongMetricValues(
+ mapOf(
+ HeartRateRecord.BPM_MIN as AggregateMetric<Any> to 53L,
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL as AggregateMetric<Any> to
+ 60_000L,
+ NutritionRecord.ENERGY_TOTAL as AggregateMetric<Any> to
+ PlatformEnergy.fromCalories(418_400.0),
+ DistanceRecord.DISTANCE_TOTAL as AggregateMetric<Any> to
+ PlatformLength.fromMeters(50.0),
+ NutritionRecord.BIOTIN_TOTAL as AggregateMetric<Any> to
+ PlatformMass.fromGrams(88.0),
+ PowerRecord.POWER_AVG as AggregateMetric<Any> to PlatformPower.fromWatts(366.0),
+ HydrationRecord.VOLUME_TOTAL as AggregateMetric<Any> to
+ PlatformVolume.fromLiters(1.5),
+ FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL as AggregateMetric<Any> to 10L,
+ BasalMetabolicRateRecord.BASAL_CALORIES_TOTAL as AggregateMetric<Any> to
+ PlatformPower.fromWatts(500.0),
+ )
+ )
+ assertThat(metricValues)
+ .containsExactly(
+ HeartRateRecord.BPM_MIN.metricKey,
+ 53L,
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL.metricKey,
+ 60_000L
+ )
+ }
+
+ @Test
+ fun getDoubleMetricValues_handlesMultipleMetrics() {
+ val metricValues =
+ getDoubleMetricValues(
+ mapOf(
+ HeartRateRecord.BPM_MIN as AggregateMetric<Any> to 53L,
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL as AggregateMetric<Any> to
+ 60_000L,
+ NutritionRecord.ENERGY_TOTAL as AggregateMetric<Any> to
+ PlatformEnergy.fromCalories(418_400.0),
+ DistanceRecord.DISTANCE_TOTAL as AggregateMetric<Any> to
+ PlatformLength.fromMeters(50.0),
+ NutritionRecord.BIOTIN_TOTAL as AggregateMetric<Any> to
+ PlatformMass.fromGrams(88.0),
+ PowerRecord.POWER_AVG as AggregateMetric<Any> to PlatformPower.fromWatts(366.0),
+ HydrationRecord.VOLUME_TOTAL as AggregateMetric<Any> to
+ PlatformVolume.fromLiters(1.5),
+ FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL as AggregateMetric<Any> to 10.0,
+ BasalMetabolicRateRecord.BASAL_CALORIES_TOTAL as AggregateMetric<Any> to
+ PlatformEnergy.fromCalories(836_800.0),
+ )
+ )
+ assertThat(metricValues)
+ .comparingValuesUsing(tolerance)
+ .containsExactly(
+ NutritionRecord.ENERGY_TOTAL.metricKey,
+ 418.4,
+ DistanceRecord.DISTANCE_TOTAL.metricKey,
+ 50.0,
+ NutritionRecord.BIOTIN_TOTAL.metricKey,
+ 88,
+ PowerRecord.POWER_AVG.metricKey,
+ 366.0,
+ HydrationRecord.VOLUME_TOTAL.metricKey,
+ 1.5,
+ FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL.metricKey,
+ 10.0,
+ BasalMetabolicRateRecord.BASAL_CALORIES_TOTAL.metricKey,
+ 836.8
+ )
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
index 96f9b86..f9d5611 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
@@ -30,6 +30,7 @@
import androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration
import androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod
import androidx.health.connect.client.impl.HealthConnectClientImpl
+import androidx.health.connect.client.impl.HealthConnectClientUpsideDownImpl
import androidx.health.connect.client.records.Record
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateGroupByDurationRequest
@@ -314,13 +315,20 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal const val DEFAULT_PROVIDER_MIN_VERSION_CODE = 35000
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ const val ACTION_HEALTH_CONNECT_SETTINGS_LEGACY =
+ "androidx.health.ACTION_HEALTH_CONNECT_SETTINGS"
+
/**
* Intent action to open Health Connect settings on this phone. Developers should use this
* if they want to re-direct the user to Health Connect.
*/
@get:JvmName("getHealthConnectSettingsAction")
@JvmStatic
- val ACTION_HEALTH_CONNECT_SETTINGS = "androidx.health.ACTION_HEALTH_CONNECT_SETTINGS"
+ val ACTION_HEALTH_CONNECT_SETTINGS =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ "android.health.connect.action.HEALTH_HOME_SETTINGS"
+ else "androidx.health.ACTION_HEALTH_CONNECT_SETTINGS"
/**
* The Health Connect SDK is not unavailable on this device at the time. This can be due to
@@ -372,51 +380,32 @@
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
): Int {
- @Suppress("Deprecation")
- if (!isApiSupported()) {
+ if (!isSdkVersionSufficient()) {
return SDK_UNAVAILABLE
}
- @Suppress("Deprecation")
if (!isProviderAvailable(context, providerPackageName)) {
return SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED
}
return SDK_AVAILABLE
}
- /**
- * Determines whether the current Health Connect SDK is supported on this device. If it is
- * not supported, then installing any provider will not help - instead disable the
- * integration.
- *
- * @return whether the api is supported on the device.
- */
- @JvmStatic
- @Deprecated("use sdkStatus()", ReplaceWith("sdkStatus(context)"))
- public fun isApiSupported(): Boolean {
- return isSdkVersionSufficient()
- }
-
- /**
- * Determines whether an implementation of [HealthConnectClient] is available on this device
- * at the moment. If none is available, apps may choose to redirect to package installers to
- * find suitable providers.
- *
- * @param context the context
- * @param providerPackageName optional package provider to choose for backend implementation
- * @return whether the api is available
- */
@JvmOverloads
@JvmStatic
- @Deprecated("use sdkStatus()", ReplaceWith("sdkStatus(context)"))
- public fun isProviderAvailable(
+ @AvailabilityStatus
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getSdkStatusLegacy(
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
- ): Boolean {
+ ): Int {
@Suppress("Deprecation")
- if (!isApiSupported()) {
- return false
+ if (!isSdkVersionSufficient()) {
+ return SDK_UNAVAILABLE
}
- return isPackageInstalled(context.packageManager, providerPackageName)
+ @Suppress("Deprecation")
+ if (!isProviderAvailableLegacy(context, providerPackageName)) {
+ return SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED
+ }
+ return SDK_AVAILABLE
}
/**
@@ -437,12 +426,34 @@
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
): HealthConnectClient {
- @Suppress("Deprecation")
- if (!isApiSupported()) {
+ val status = getSdkStatus(context, providerPackageName)
+ if (status == SDK_UNAVAILABLE) {
throw UnsupportedOperationException("SDK version too low")
}
- @Suppress("Deprecation")
- if (!isProviderAvailable(context, providerPackageName)) {
+ if (status == SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) {
+ throw IllegalStateException("Service not available")
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return HealthConnectClientUpsideDownImpl(context)
+ }
+ return HealthConnectClientImpl(
+ HealthDataService.getClient(context, providerPackageName)
+ )
+ }
+
+ @JvmOverloads
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun getOrCreateLegacy(
+ context: Context,
+ providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
+ ): HealthConnectClient {
+ val status = getSdkStatusLegacy(context, providerPackageName)
+ if (status == SDK_UNAVAILABLE) {
+ throw UnsupportedOperationException("SDK version too low")
+ }
+ if (status == SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) {
throw IllegalStateException("Service not available")
}
return HealthConnectClientImpl(
@@ -453,7 +464,28 @@
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
internal fun isSdkVersionSufficient() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
- internal fun isPackageInstalled(
+ /**
+ * Determines whether an implementation of [HealthConnectClient] is available on this device
+ * at the moment.
+ */
+ internal fun isProviderAvailable(
+ context: Context,
+ providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
+ ): Boolean {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return true
+ }
+ return isPackageInstalled(context.packageManager, providerPackageName)
+ }
+
+ internal fun isProviderAvailableLegacy(
+ context: Context,
+ providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
+ ): Boolean {
+ return isPackageInstalled(context.packageManager, providerPackageName)
+ }
+
+ private fun isPackageInstalled(
packageManager: PackageManager,
packageName: String,
): Boolean {
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt
index 1be95806..d183e7c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt
@@ -16,9 +16,13 @@
package androidx.health.connect.client
import androidx.activity.result.contract.ActivityResultContract
+import androidx.annotation.RestrictTo
+import androidx.core.os.BuildCompat
+import androidx.core.os.BuildCompat.PrereleaseSdkCheck
import androidx.health.connect.client.HealthConnectClient.Companion.DEFAULT_PROVIDER_PACKAGE_NAME
import androidx.health.connect.client.permission.HealthDataRequestPermissionsInternal
import androidx.health.connect.client.permission.HealthPermission
+import androidx.health.connect.client.permission.platform.HealthDataRequestPermissionsUpsideDownCake
@JvmDefaultWithCompatibility
/** Interface for operations related to permissions. */
@@ -45,6 +49,17 @@
suspend fun revokeAllPermissions()
companion object {
+
+ @JvmStatic
+ @JvmOverloads
+ @Suppress("IllegalExperimentalApiUsage")
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun createRequestPermissionResultContractLegacy(
+ providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME
+ ): ActivityResultContract<Set<String>, Set<String>> {
+ return HealthDataRequestPermissionsInternal(providerPackageName = providerPackageName)
+ }
+
/**
* Creates an [ActivityResultContract] to request Health permissions.
*
@@ -55,9 +70,14 @@
*/
@JvmStatic
@JvmOverloads
+ @PrereleaseSdkCheck
+ @Suppress("IllegalExperimentalApiUsage")
fun createRequestPermissionResultContract(
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME
): ActivityResultContract<Set<String>, Set<String>> {
+ if (BuildCompat.isAtLeastU()) {
+ return HealthDataRequestPermissionsUpsideDownCake()
+ }
return HealthDataRequestPermissionsInternal(providerPackageName = providerPackageName)
}
}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt
new file mode 100644
index 0000000..8c2d24c
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientUpsideDownImpl.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.impl
+
+import android.content.Context
+import android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED
+import android.content.pm.PackageManager.GET_PERMISSIONS
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.health.connect.HealthConnectException
+import android.health.connect.HealthConnectManager
+import android.health.connect.ReadRecordsRequestUsingIds
+import android.health.connect.RecordIdFilter
+import android.health.connect.changelog.ChangeLogsRequest
+import android.os.RemoteException
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.core.os.asOutcomeReceiver
+import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.PermissionController
+import androidx.health.connect.client.aggregate.AggregationResult
+import androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration
+import androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod
+import androidx.health.connect.client.changes.DeletionChange
+import androidx.health.connect.client.changes.UpsertionChange
+import androidx.health.connect.client.impl.platform.records.toPlatformRecord
+import androidx.health.connect.client.impl.platform.records.toPlatformRecordClass
+import androidx.health.connect.client.impl.platform.records.toPlatformRequest
+import androidx.health.connect.client.impl.platform.records.toPlatformTimeRangeFilter
+import androidx.health.connect.client.impl.platform.records.toSdkRecord
+import androidx.health.connect.client.impl.platform.records.toSdkResponse
+import androidx.health.connect.client.impl.platform.response.toKtResponse
+import androidx.health.connect.client.impl.platform.toKtException
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_PREFIX
+import androidx.health.connect.client.records.Record
+import androidx.health.connect.client.request.AggregateGroupByDurationRequest
+import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ChangesTokenRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
+import androidx.health.connect.client.response.ChangesResponse
+import androidx.health.connect.client.response.InsertRecordsResponse
+import androidx.health.connect.client.response.ReadRecordResponse
+import androidx.health.connect.client.response.ReadRecordsResponse
+import androidx.health.connect.client.time.TimeRangeFilter
+import kotlin.reflect.KClass
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Implements the [HealthConnectClient] with APIs in UpsideDownCake.
+ *
+ * @suppress
+ */
+@RequiresApi(api = 34)
+class HealthConnectClientUpsideDownImpl : HealthConnectClient, PermissionController {
+
+ private val executor = Dispatchers.Default.asExecutor()
+
+ private val context: Context
+ private val healthConnectManager: HealthConnectManager
+ private val revokePermissionsFunction: (Collection<String>) -> Unit
+
+ constructor(context: Context) : this(context, context::revokeSelfPermissionsOnKill)
+
+ @VisibleForTesting
+ internal constructor(
+ context: Context,
+ revokePermissionsFunction: (Collection<String>) -> Unit
+ ) {
+ this.context = context
+ this.healthConnectManager =
+ context.getSystemService(Context.HEALTHCONNECT_SERVICE) as HealthConnectManager
+ this.revokePermissionsFunction = revokePermissionsFunction
+ }
+
+ override val permissionController: PermissionController
+ get() = this
+
+ override suspend fun insertRecords(records: List<Record>): InsertRecordsResponse {
+ val response = wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.insertRecords(
+ records.map { it.toPlatformRecord() },
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ return response.toKtResponse()
+ }
+
+ override suspend fun updateRecords(records: List<Record>) {
+ wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.updateRecords(
+ records.map { it.toPlatformRecord() },
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ }
+
+ override suspend fun deleteRecords(
+ recordType: KClass<out Record>,
+ recordIdsList: List<String>,
+ clientRecordIdsList: List<String>
+ ) {
+ wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.deleteRecords(
+ buildList {
+ recordIdsList.forEach {
+ add(RecordIdFilter.fromId(recordType.toPlatformRecordClass(), it))
+ }
+ clientRecordIdsList.forEach {
+ add(
+ RecordIdFilter.fromClientRecordId(
+ recordType.toPlatformRecordClass(),
+ it
+ )
+ )
+ }
+ },
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ }
+
+ override suspend fun deleteRecords(
+ recordType: KClass<out Record>,
+ timeRangeFilter: TimeRangeFilter
+ ) {
+ wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.deleteRecords(
+ recordType.toPlatformRecordClass(),
+ timeRangeFilter.toPlatformTimeRangeFilter(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST") // Safe to cast as the type should match
+ override suspend fun <T : Record> readRecord(
+ recordType: KClass<T>,
+ recordId: String
+ ): ReadRecordResponse<T> {
+ val response = wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.readRecords(
+ ReadRecordsRequestUsingIds.Builder(recordType.toPlatformRecordClass())
+ .addId(recordId)
+ .build(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ if (response.records.isEmpty()) {
+ throw RemoteException("No records")
+ }
+ return ReadRecordResponse(response.records[0].toSdkRecord() as T)
+ }
+
+ @Suppress("UNCHECKED_CAST") // Safe to cast as the type should match
+ override suspend fun <T : Record> readRecords(
+ request: ReadRecordsRequest<T>
+ ): ReadRecordsResponse<T> {
+ val response = wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.readRecords(
+ request.toPlatformRequest(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ return ReadRecordsResponse(
+ response.records.map { it.toSdkRecord() as T },
+ pageToken = response.nextPageToken.takeUnless { it == -1L }?.toString()
+ )
+ }
+
+ override suspend fun aggregate(request: AggregateRequest): AggregationResult {
+ return wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.aggregate(
+ request.toPlatformRequest(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ .toSdkResponse(request.metrics)
+ }
+
+ override suspend fun aggregateGroupByDuration(
+ request: AggregateGroupByDurationRequest
+ ): List<AggregationResultGroupedByDuration> {
+ return wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.aggregateGroupByDuration(
+ request.toPlatformRequest(),
+ request.timeRangeSlicer,
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ .map { it.toSdkResponse(request.metrics) }
+ }
+
+ override suspend fun aggregateGroupByPeriod(
+ request: AggregateGroupByPeriodRequest
+ ): List<AggregationResultGroupedByPeriod> {
+ return wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.aggregateGroupByPeriod(
+ request.toPlatformRequest(),
+ request.timeRangeSlicer,
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ .map { it.toSdkResponse(request.metrics) }
+ }
+
+ override suspend fun getChangesToken(request: ChangesTokenRequest): String {
+ return wrapPlatformException {
+ suspendCancellableCoroutine { continuation ->
+ healthConnectManager.getChangeLogToken(
+ request.toPlatformRequest(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ }
+ .token
+ }
+
+ override suspend fun getChanges(changesToken: String): ChangesResponse {
+ try {
+ val response = suspendCancellableCoroutine { continuation ->
+ healthConnectManager.getChangeLogs(
+ ChangeLogsRequest.Builder(changesToken).build(),
+ executor,
+ continuation.asOutcomeReceiver()
+ )
+ }
+ return ChangesResponse(
+ buildList {
+ response.upsertedRecords.forEach { add(UpsertionChange(it.toSdkRecord())) }
+ response.deletedLogs.forEach { add(DeletionChange(it.deletedRecordId)) }
+ },
+ response.nextChangesToken,
+ response.hasMorePages(),
+ changesTokenExpired = false
+ )
+ } catch (e: HealthConnectException) {
+ // Handle invalid token
+ if (e.errorCode == HealthConnectException.ERROR_INVALID_ARGUMENT) {
+ return ChangesResponse(
+ changes = listOf(),
+ nextChangesToken = "",
+ hasMore = false,
+ changesTokenExpired = true
+ )
+ }
+ throw e.toKtException()
+ }
+ }
+
+ override suspend fun getGrantedPermissions(): Set<String> {
+ context.packageManager
+ .getPackageInfo(context.packageName, PackageInfoFlags.of(GET_PERMISSIONS.toLong()))
+ .let {
+ return buildSet {
+ for (i in it.requestedPermissions.indices) {
+ if (
+ it.requestedPermissions[i].startsWith(PERMISSION_PREFIX) &&
+ it.requestedPermissionsFlags[i] and REQUESTED_PERMISSION_GRANTED > 0
+ ) {
+ add(it.requestedPermissions[i])
+ }
+ }
+ }
+ }
+ }
+
+ override suspend fun revokeAllPermissions() {
+ val allHealthPermissions =
+ context.packageManager
+ .getPackageInfo(context.packageName, PackageInfoFlags.of(GET_PERMISSIONS.toLong()))
+ .requestedPermissions
+ .filter { it.startsWith(PERMISSION_PREFIX) }
+ revokePermissionsFunction(allHealthPermissions)
+ }
+
+ private suspend fun <T> wrapPlatformException(function: suspend () -> T): T {
+ return try {
+ function()
+ } catch (e: HealthConnectException) {
+ throw e.toKtException()
+ }
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt
new file mode 100644
index 0000000..be40e96
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform
+
+import android.health.connect.HealthConnectException
+import android.os.RemoteException
+import androidx.annotation.RequiresApi
+import java.io.IOException
+import java.lang.IllegalArgumentException
+import java.lang.IllegalStateException
+
+/** Converts exception returned by the platform to one of standard exception class hierarchy. */
+internal fun HealthConnectException.toKtException(): Exception {
+ return when (errorCode) {
+ HealthConnectException.ERROR_IO -> IOException(message)
+ HealthConnectException.ERROR_REMOTE -> RemoteException(message)
+ HealthConnectException.ERROR_SECURITY -> SecurityException(message)
+ HealthConnectException.ERROR_INVALID_ARGUMENT -> IllegalArgumentException(message)
+ else -> IllegalStateException(message)
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/package-info.java b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/package-info.java
new file mode 100644
index 0000000..dc32f56
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Helps with conversions to the platform record and API objects.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.health.connect.client.impl.platform;
+
+import androidx.annotation.RestrictTo;
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/AggregationMappings.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/AggregationMappings.kt
new file mode 100644
index 0000000..42d97d3
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/AggregationMappings.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.health.connect.datatypes.ActiveCaloriesBurnedRecord as PlatformActiveCaloriesBurnedRecord
+import android.health.connect.datatypes.AggregationType as PlatformAggregateMetric
+import android.health.connect.datatypes.BasalMetabolicRateRecord as PlatformBasalMetabolicRateRecord
+import android.health.connect.datatypes.DistanceRecord as PlatformDistanceRecord
+import android.health.connect.datatypes.ElevationGainedRecord as PlatformElevationGainedRecord
+import android.health.connect.datatypes.FloorsClimbedRecord as PlatformFloorsClimbedRecord
+import android.health.connect.datatypes.HeartRateRecord as PlatformHeartRateRecord
+import android.health.connect.datatypes.HeightRecord as PlatformHeightRecord
+import android.health.connect.datatypes.HydrationRecord as PlatformHydrationRecord
+import android.health.connect.datatypes.NutritionRecord as PlatformNutritionRecord
+import android.health.connect.datatypes.PowerRecord as PlatformPowerRecord
+import android.health.connect.datatypes.StepsRecord as PlatformStepsRecord
+import android.health.connect.datatypes.TotalCaloriesBurnedRecord as PlatformTotalCaloriesBurnedRecord
+import android.health.connect.datatypes.WeightRecord as PlatformWeightRecord
+import android.health.connect.datatypes.WheelchairPushesRecord as PlatformWheelchairPushesRecord
+import android.health.connect.datatypes.units.Energy as PlatformEnergy
+import android.health.connect.datatypes.units.Length as PlatformLength
+import android.health.connect.datatypes.units.Mass as PlatformMass
+import android.health.connect.datatypes.units.Power as PlatformPower
+import android.health.connect.datatypes.units.Volume as PlatformVolume
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.aggregate.AggregateMetric
+import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
+import androidx.health.connect.client.records.BasalMetabolicRateRecord
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.ElevationGainedRecord
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.FloorsClimbedRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.HeightRecord
+import androidx.health.connect.client.records.HydrationRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.PowerRecord
+import androidx.health.connect.client.records.RestingHeartRateRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
+import androidx.health.connect.client.records.WeightRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.Length
+import androidx.health.connect.client.units.Mass
+import androidx.health.connect.client.units.Power
+import androidx.health.connect.client.units.Volume
+import java.time.Duration
+
+internal val DOUBLE_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Double>, PlatformAggregateMetric<Double>> =
+ mapOf(
+ FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL to
+ PlatformFloorsClimbedRecord.FLOORS_CLIMBED_TOTAL,
+ )
+
+internal val DURATION_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Duration>, PlatformAggregateMetric<Long>> =
+ mapOf(
+ ExerciseSessionRecord.EXERCISE_DURATION_TOTAL to
+ PlatformExerciseSessionRecord.EXERCISE_DURATION_TOTAL,
+ SleepSessionRecord.SLEEP_DURATION_TOTAL to PlatformSleepSessionRecord.SLEEP_DURATION_TOTAL
+ )
+
+internal val ENERGY_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Energy>, PlatformAggregateMetric<PlatformEnergy>> =
+ mapOf(
+ ActiveCaloriesBurnedRecord.ACTIVE_CALORIES_TOTAL to
+ PlatformActiveCaloriesBurnedRecord.ACTIVE_CALORIES_TOTAL,
+ BasalMetabolicRateRecord.BASAL_CALORIES_TOTAL to
+ PlatformBasalMetabolicRateRecord.BASAL_CALORIES_TOTAL,
+ NutritionRecord.ENERGY_TOTAL to PlatformNutritionRecord.ENERGY_TOTAL,
+ NutritionRecord.ENERGY_FROM_FAT_TOTAL to PlatformNutritionRecord.ENERGY_FROM_FAT_TOTAL,
+ TotalCaloriesBurnedRecord.ENERGY_TOTAL to PlatformTotalCaloriesBurnedRecord.ENERGY_TOTAL,
+ )
+
+internal val LENGTH_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Length>, PlatformAggregateMetric<PlatformLength>> =
+ mapOf(
+ DistanceRecord.DISTANCE_TOTAL to PlatformDistanceRecord.DISTANCE_TOTAL,
+ ElevationGainedRecord.ELEVATION_GAINED_TOTAL to
+ PlatformElevationGainedRecord.ELEVATION_GAINED_TOTAL,
+ HeightRecord.HEIGHT_AVG to PlatformHeightRecord.HEIGHT_AVG,
+ HeightRecord.HEIGHT_MIN to PlatformHeightRecord.HEIGHT_MIN,
+ HeightRecord.HEIGHT_MAX to PlatformHeightRecord.HEIGHT_MAX,
+ )
+
+internal val LONG_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Long>, PlatformAggregateMetric<Long>> =
+ mapOf(
+ HeartRateRecord.BPM_AVG to PlatformHeartRateRecord.BPM_AVG,
+ HeartRateRecord.BPM_MIN to PlatformHeartRateRecord.BPM_MIN,
+ HeartRateRecord.BPM_MAX to PlatformHeartRateRecord.BPM_MAX,
+ HeartRateRecord.MEASUREMENTS_COUNT to PlatformHeartRateRecord.HEART_MEASUREMENTS_COUNT,
+ RestingHeartRateRecord.BPM_AVG to PlatformRestingHeartRateRecord.BPM_AVG,
+ RestingHeartRateRecord.BPM_MIN to PlatformRestingHeartRateRecord.BPM_MIN,
+ RestingHeartRateRecord.BPM_MAX to PlatformRestingHeartRateRecord.BPM_MAX,
+ StepsRecord.COUNT_TOTAL to PlatformStepsRecord.STEPS_COUNT_TOTAL,
+ WheelchairPushesRecord.COUNT_TOTAL to
+ PlatformWheelchairPushesRecord.WHEEL_CHAIR_PUSHES_COUNT_TOTAL,
+ )
+
+internal val GRAMS_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Mass>, PlatformAggregateMetric<PlatformMass>> =
+ mapOf(
+ NutritionRecord.BIOTIN_TOTAL to PlatformNutritionRecord.BIOTIN_TOTAL,
+ NutritionRecord.CAFFEINE_TOTAL to PlatformNutritionRecord.CAFFEINE_TOTAL,
+ NutritionRecord.CALCIUM_TOTAL to PlatformNutritionRecord.CALCIUM_TOTAL,
+ NutritionRecord.CHLORIDE_TOTAL to PlatformNutritionRecord.CHLORIDE_TOTAL,
+ NutritionRecord.CHOLESTEROL_TOTAL to PlatformNutritionRecord.CHOLESTEROL_TOTAL,
+ NutritionRecord.CHROMIUM_TOTAL to PlatformNutritionRecord.CHROMIUM_TOTAL,
+ NutritionRecord.COPPER_TOTAL to PlatformNutritionRecord.COPPER_TOTAL,
+ NutritionRecord.DIETARY_FIBER_TOTAL to PlatformNutritionRecord.DIETARY_FIBER_TOTAL,
+ NutritionRecord.FOLATE_TOTAL to PlatformNutritionRecord.FOLATE_TOTAL,
+ NutritionRecord.FOLIC_ACID_TOTAL to PlatformNutritionRecord.FOLIC_ACID_TOTAL,
+ NutritionRecord.IODINE_TOTAL to PlatformNutritionRecord.IODINE_TOTAL,
+ NutritionRecord.IRON_TOTAL to PlatformNutritionRecord.IRON_TOTAL,
+ NutritionRecord.MAGNESIUM_TOTAL to PlatformNutritionRecord.MAGNESIUM_TOTAL,
+ NutritionRecord.MANGANESE_TOTAL to PlatformNutritionRecord.MANGANESE_TOTAL,
+ NutritionRecord.MOLYBDENUM_TOTAL to PlatformNutritionRecord.MOLYBDENUM_TOTAL,
+ NutritionRecord.MONOUNSATURATED_FAT_TOTAL to
+ PlatformNutritionRecord.MONOUNSATURATED_FAT_TOTAL,
+ NutritionRecord.NIACIN_TOTAL to PlatformNutritionRecord.NIACIN_TOTAL,
+ NutritionRecord.PANTOTHENIC_ACID_TOTAL to PlatformNutritionRecord.PANTOTHENIC_ACID_TOTAL,
+ NutritionRecord.PHOSPHORUS_TOTAL to PlatformNutritionRecord.PHOSPHORUS_TOTAL,
+ NutritionRecord.POLYUNSATURATED_FAT_TOTAL to
+ PlatformNutritionRecord.POLYUNSATURATED_FAT_TOTAL,
+ NutritionRecord.POTASSIUM_TOTAL to PlatformNutritionRecord.POTASSIUM_TOTAL,
+ NutritionRecord.PROTEIN_TOTAL to PlatformNutritionRecord.PROTEIN_TOTAL,
+ NutritionRecord.RIBOFLAVIN_TOTAL to PlatformNutritionRecord.RIBOFLAVIN_TOTAL,
+ NutritionRecord.SATURATED_FAT_TOTAL to PlatformNutritionRecord.SATURATED_FAT_TOTAL,
+ NutritionRecord.SELENIUM_TOTAL to PlatformNutritionRecord.SELENIUM_TOTAL,
+ NutritionRecord.SODIUM_TOTAL to PlatformNutritionRecord.SODIUM_TOTAL,
+ NutritionRecord.SUGAR_TOTAL to PlatformNutritionRecord.SUGAR_TOTAL,
+ NutritionRecord.THIAMIN_TOTAL to PlatformNutritionRecord.THIAMIN_TOTAL,
+ NutritionRecord.TOTAL_CARBOHYDRATE_TOTAL to
+ PlatformNutritionRecord.TOTAL_CARBOHYDRATE_TOTAL,
+ NutritionRecord.TOTAL_FAT_TOTAL to PlatformNutritionRecord.TOTAL_FAT_TOTAL,
+ NutritionRecord.UNSATURATED_FAT_TOTAL to PlatformNutritionRecord.UNSATURATED_FAT_TOTAL,
+ NutritionRecord.VITAMIN_A_TOTAL to PlatformNutritionRecord.VITAMIN_A_TOTAL,
+ NutritionRecord.VITAMIN_B12_TOTAL to PlatformNutritionRecord.VITAMIN_B12_TOTAL,
+ NutritionRecord.VITAMIN_B6_TOTAL to PlatformNutritionRecord.VITAMIN_B6_TOTAL,
+ NutritionRecord.VITAMIN_C_TOTAL to PlatformNutritionRecord.VITAMIN_C_TOTAL,
+ NutritionRecord.VITAMIN_D_TOTAL to PlatformNutritionRecord.VITAMIN_D_TOTAL,
+ NutritionRecord.VITAMIN_E_TOTAL to PlatformNutritionRecord.VITAMIN_E_TOTAL,
+ NutritionRecord.VITAMIN_K_TOTAL to PlatformNutritionRecord.VITAMIN_K_TOTAL,
+ NutritionRecord.ZINC_TOTAL to PlatformNutritionRecord.ZINC_TOTAL
+ )
+
+internal val KILOGRAMS_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Mass>, PlatformAggregateMetric<PlatformMass>> =
+ mapOf(
+ WeightRecord.WEIGHT_AVG to PlatformWeightRecord.WEIGHT_AVG,
+ WeightRecord.WEIGHT_MIN to PlatformWeightRecord.WEIGHT_MIN,
+ WeightRecord.WEIGHT_MAX to PlatformWeightRecord.WEIGHT_MAX,
+ )
+
+internal val POWER_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Power>, PlatformAggregateMetric<PlatformPower>> =
+ mapOf(
+ PowerRecord.POWER_AVG to PlatformPowerRecord.POWER_AVG,
+ PowerRecord.POWER_MAX to PlatformPowerRecord.POWER_MAX,
+ PowerRecord.POWER_MIN to PlatformPowerRecord.POWER_MIN,
+ )
+
+internal val VOLUME_AGGREGATION_METRIC_TYPE_MAP:
+ Map<AggregateMetric<Volume>, PlatformAggregateMetric<PlatformVolume>> =
+ mapOf(
+ HydrationRecord.VOLUME_TOTAL to PlatformHydrationRecord.VOLUME_TOTAL,
+ )
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/IntDefMappings.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/IntDefMappings.kt
new file mode 100644
index 0000000..0ad31b2
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/IntDefMappings.kt
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.records.BloodGlucoseRecord
+import androidx.health.connect.client.records.BloodPressureRecord
+import androidx.health.connect.client.records.BodyTemperatureMeasurementLocation
+import androidx.health.connect.client.records.CervicalMucusRecord
+import androidx.health.connect.client.records.ExerciseSegment
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.MealType
+import androidx.health.connect.client.records.MenstruationFlowRecord
+import androidx.health.connect.client.records.OvulationTestRecord
+import androidx.health.connect.client.records.SexualActivityRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.Vo2MaxRecord
+import androidx.health.connect.client.records.metadata.Metadata
+
+internal val SDK_TO_PLATFORM_CERVICAL_MUCUS_APPEARANCE: Map<Int, Int> =
+ mapOf(
+ CervicalMucusRecord.APPEARANCE_EGG_WHITE to
+ PlatformCervicalMucusAppearance.APPEARANCE_EGG_WHITE,
+ CervicalMucusRecord.APPEARANCE_DRY to PlatformCervicalMucusAppearance.APPEARANCE_DRY,
+ CervicalMucusRecord.APPEARANCE_STICKY to PlatformCervicalMucusAppearance.APPEARANCE_STICKY,
+ CervicalMucusRecord.APPEARANCE_CREAMY to PlatformCervicalMucusAppearance.APPEARANCE_CREAMY,
+ CervicalMucusRecord.APPEARANCE_WATERY to PlatformCervicalMucusAppearance.APPEARANCE_WATERY,
+ CervicalMucusRecord.APPEARANCE_UNUSUAL to
+ PlatformCervicalMucusAppearance.APPEARANCE_UNUSUAL,
+ )
+
+internal val PLATFORM_TO_SDK_CERVICAL_MUCUS_APPEARANCE =
+ SDK_TO_PLATFORM_CERVICAL_MUCUS_APPEARANCE.reversed()
+
+internal val SDK_TO_PLATFORM_BLOOD_PRESSURE_BODY_POSITION: Map<Int, Int> =
+ mapOf(
+ BloodPressureRecord.BODY_POSITION_STANDING_UP to
+ PlatformBloodPressureBodyPosition.BODY_POSITION_STANDING_UP,
+ BloodPressureRecord.BODY_POSITION_SITTING_DOWN to
+ PlatformBloodPressureBodyPosition.BODY_POSITION_SITTING_DOWN,
+ BloodPressureRecord.BODY_POSITION_LYING_DOWN to
+ PlatformBloodPressureBodyPosition.BODY_POSITION_LYING_DOWN,
+ BloodPressureRecord.BODY_POSITION_RECLINING to
+ PlatformBloodPressureBodyPosition.BODY_POSITION_RECLINING,
+ )
+
+internal val PLATFORM_TO_SDK_BLOOD_PRESSURE_BODY_POSITION =
+ SDK_TO_PLATFORM_BLOOD_PRESSURE_BODY_POSITION.reversed()
+
+internal val SDK_TO_PLATFORM_EXERCISE_SESSION_TYPE: Map<Int, Int> =
+ mapOf(
+ ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_OTHER_WORKOUT,
+ ExerciseSessionRecord.EXERCISE_TYPE_BADMINTON to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BADMINTON,
+ ExerciseSessionRecord.EXERCISE_TYPE_BASEBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BASEBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_BASKETBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BASKETBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_BIKING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BIKING,
+ ExerciseSessionRecord.EXERCISE_TYPE_BIKING_STATIONARY to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BIKING_STATIONARY,
+ ExerciseSessionRecord.EXERCISE_TYPE_BOOT_CAMP to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BOOT_CAMP,
+ ExerciseSessionRecord.EXERCISE_TYPE_BOXING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_BOXING,
+ ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_CALISTHENICS,
+ ExerciseSessionRecord.EXERCISE_TYPE_CRICKET to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_CRICKET,
+ ExerciseSessionRecord.EXERCISE_TYPE_DANCING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_DANCING,
+ ExerciseSessionRecord.EXERCISE_TYPE_ELLIPTICAL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ELLIPTICAL,
+ ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_EXERCISE_CLASS,
+ ExerciseSessionRecord.EXERCISE_TYPE_FENCING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_FENCING,
+ ExerciseSessionRecord.EXERCISE_TYPE_FOOTBALL_AMERICAN to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN,
+ ExerciseSessionRecord.EXERCISE_TYPE_FOOTBALL_AUSTRALIAN to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_FOOTBALL_AUSTRALIAN,
+ ExerciseSessionRecord.EXERCISE_TYPE_FRISBEE_DISC to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_FRISBEE_DISC,
+ ExerciseSessionRecord.EXERCISE_TYPE_GOLF to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_GOLF,
+ ExerciseSessionRecord.EXERCISE_TYPE_GUIDED_BREATHING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_GUIDED_BREATHING,
+ ExerciseSessionRecord.EXERCISE_TYPE_GYMNASTICS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_GYMNASTICS,
+ ExerciseSessionRecord.EXERCISE_TYPE_HANDBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_HANDBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+ ExerciseSessionRecord.EXERCISE_TYPE_HIKING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_HIKING,
+ ExerciseSessionRecord.EXERCISE_TYPE_ICE_HOCKEY to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ICE_HOCKEY,
+ ExerciseSessionRecord.EXERCISE_TYPE_ICE_SKATING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ICE_SKATING,
+ ExerciseSessionRecord.EXERCISE_TYPE_MARTIAL_ARTS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_MARTIAL_ARTS,
+ ExerciseSessionRecord.EXERCISE_TYPE_PADDLING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_PADDLING,
+ ExerciseSessionRecord.EXERCISE_TYPE_PARAGLIDING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_PARAGLIDING,
+ ExerciseSessionRecord.EXERCISE_TYPE_PILATES to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_PILATES,
+ ExerciseSessionRecord.EXERCISE_TYPE_RACQUETBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_RACQUETBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_ROCK_CLIMBING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ROCK_CLIMBING,
+ ExerciseSessionRecord.EXERCISE_TYPE_ROLLER_HOCKEY to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ROLLER_HOCKEY,
+ ExerciseSessionRecord.EXERCISE_TYPE_ROWING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ROWING,
+ ExerciseSessionRecord.EXERCISE_TYPE_ROWING_MACHINE to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_ROWING_MACHINE,
+ ExerciseSessionRecord.EXERCISE_TYPE_RUGBY to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_RUGBY,
+ ExerciseSessionRecord.EXERCISE_TYPE_RUNNING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING,
+ ExerciseSessionRecord.EXERCISE_TYPE_RUNNING_TREADMILL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING_TREADMILL,
+ ExerciseSessionRecord.EXERCISE_TYPE_SAILING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SAILING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SCUBA_DIVING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SCUBA_DIVING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SKATING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SKATING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SKIING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SKIING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SNOWBOARDING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SNOWBOARDING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SNOWSHOEING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SNOWSHOEING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SOCCER to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SOCCER,
+ ExerciseSessionRecord.EXERCISE_TYPE_SOFTBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SOFTBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_SQUASH to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SQUASH,
+ ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_STAIR_CLIMBING,
+ ExerciseSessionRecord.EXERCISE_TYPE_STAIR_CLIMBING_MACHINE to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE,
+ ExerciseSessionRecord.EXERCISE_TYPE_STRENGTH_TRAINING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_STRENGTH_TRAINING,
+ ExerciseSessionRecord.EXERCISE_TYPE_STRETCHING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_STRETCHING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SURFING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SURFING,
+ ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SWIMMING_OPEN_WATER,
+ ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_POOL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_SWIMMING_POOL,
+ ExerciseSessionRecord.EXERCISE_TYPE_TABLE_TENNIS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_TABLE_TENNIS,
+ ExerciseSessionRecord.EXERCISE_TYPE_TENNIS to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_TENNIS,
+ ExerciseSessionRecord.EXERCISE_TYPE_VOLLEYBALL to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_VOLLEYBALL,
+ ExerciseSessionRecord.EXERCISE_TYPE_WALKING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_WALKING,
+ ExerciseSessionRecord.EXERCISE_TYPE_WATER_POLO to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_WATER_POLO,
+ ExerciseSessionRecord.EXERCISE_TYPE_WEIGHTLIFTING to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_WEIGHTLIFTING,
+ ExerciseSessionRecord.EXERCISE_TYPE_WHEELCHAIR to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_WHEELCHAIR,
+ ExerciseSessionRecord.EXERCISE_TYPE_YOGA to
+ PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_YOGA,
+ )
+
+internal val PLATFORM_TO_SDK_EXERCISE_SESSION_TYPE =
+ SDK_TO_PLATFORM_EXERCISE_SESSION_TYPE.reversed()
+
+internal val SDK_TO_PLATFORM_MEAL_TYPE: Map<Int, Int> =
+ mapOf(
+ MealType.MEAL_TYPE_BREAKFAST to PlatformMealType.MEAL_TYPE_BREAKFAST,
+ MealType.MEAL_TYPE_LUNCH to PlatformMealType.MEAL_TYPE_LUNCH,
+ MealType.MEAL_TYPE_DINNER to PlatformMealType.MEAL_TYPE_DINNER,
+ MealType.MEAL_TYPE_SNACK to PlatformMealType.MEAL_TYPE_SNACK,
+ )
+
+internal val PLATFORM_TO_SDK_MEAL_TYPE = SDK_TO_PLATFORM_MEAL_TYPE.reversed()
+
+internal val SDK_TO_PLATFORM_VO2_MAX_MEASUREMENT_METHOD: Map<Int, Int> =
+ mapOf(
+ Vo2MaxRecord.MEASUREMENT_METHOD_METABOLIC_CART to
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_METABOLIC_CART,
+ Vo2MaxRecord.MEASUREMENT_METHOD_HEART_RATE_RATIO to
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_HEART_RATE_RATIO,
+ Vo2MaxRecord.MEASUREMENT_METHOD_COOPER_TEST to
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_COOPER_TEST,
+ Vo2MaxRecord.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST to
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_MULTISTAGE_FITNESS_TEST,
+ Vo2MaxRecord.MEASUREMENT_METHOD_ROCKPORT_FITNESS_TEST to
+ PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_ROCKPORT_FITNESS_TEST,
+ )
+
+internal val PLATFORM_TO_SDK_VO2_MAX_MEASUREMENT_METHOD =
+ SDK_TO_PLATFORM_VO2_MAX_MEASUREMENT_METHOD.reversed()
+
+internal val SDK_TO_PLATFORM_MENSTRUATION_FLOW_TYPE: Map<Int, Int> =
+ mapOf(
+ MenstruationFlowRecord.FLOW_LIGHT to PlatformMenstruationFlowType.FLOW_LIGHT,
+ MenstruationFlowRecord.FLOW_MEDIUM to PlatformMenstruationFlowType.FLOW_MEDIUM,
+ MenstruationFlowRecord.FLOW_HEAVY to PlatformMenstruationFlowType.FLOW_HEAVY,
+ )
+
+internal val PLATFORM_TO_SDK_MENSTRUATION_FLOW_TYPE =
+ SDK_TO_PLATFORM_MENSTRUATION_FLOW_TYPE.reversed()
+
+internal val SDK_TO_PLATFORM_BODY_TEMPERATURE_MEASUREMENT_LOCATION: Map<Int, Int> =
+ mapOf(
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_ARMPIT to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_ARMPIT,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FINGER to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FINGER,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FOREHEAD to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_FOREHEAD,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_MOUTH to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_MOUTH,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_RECTUM to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_RECTUM,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_TEMPORAL_ARTERY to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_TEMPORAL_ARTERY,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_TOE to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_TOE,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_EAR to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_EAR,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_WRIST to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_WRIST,
+ BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_VAGINA to
+ PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_VAGINA,
+ )
+
+internal val PLATFORM_TO_SDK_BODY_TEMPERATURE_MEASUREMENT_LOCATION =
+ SDK_TO_PLATFORM_BODY_TEMPERATURE_MEASUREMENT_LOCATION.reversed()
+
+internal val SDK_TO_PLATFORM_BLOOD_PRESSURE_MEASUREMENT_LOCATION: Map<Int, Int> =
+ mapOf(
+ BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST to
+ PlatformBloodPressureMeasurementLocation.BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST,
+ BloodPressureRecord.MEASUREMENT_LOCATION_RIGHT_WRIST to
+ PlatformBloodPressureMeasurementLocation
+ .BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_WRIST,
+ BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_UPPER_ARM to
+ PlatformBloodPressureMeasurementLocation
+ .BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_UPPER_ARM,
+ BloodPressureRecord.MEASUREMENT_LOCATION_RIGHT_UPPER_ARM to
+ PlatformBloodPressureMeasurementLocation
+ .BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_UPPER_ARM,
+ )
+
+internal val PLATFORM_TO_SDK_BLOOD_PRESSURE_MEASUREMENT_LOCATION =
+ SDK_TO_PLATFORM_BLOOD_PRESSURE_MEASUREMENT_LOCATION.reversed()
+
+internal val SDK_TO_PLATFORM_OVULATION_TEST_RESULT: Map<Int, Int> =
+ mapOf(
+ OvulationTestRecord.RESULT_POSITIVE to PlatformOvulationTestResult.RESULT_POSITIVE,
+ OvulationTestRecord.RESULT_HIGH to PlatformOvulationTestResult.RESULT_HIGH,
+ OvulationTestRecord.RESULT_NEGATIVE to PlatformOvulationTestResult.RESULT_NEGATIVE,
+ OvulationTestRecord.RESULT_INCONCLUSIVE to PlatformOvulationTestResult.RESULT_INCONCLUSIVE,
+ )
+
+internal val PLATFORM_TO_SDK_OVULATION_TEST_RESULT =
+ SDK_TO_PLATFORM_OVULATION_TEST_RESULT.reversed()
+
+internal val SDK_TO_PLATFORM_CERVICAL_MUCUS_SENSATION: Map<Int, Int> =
+ mapOf(
+ CervicalMucusRecord.SENSATION_LIGHT to PlatformCervicalMucusSensation.SENSATION_LIGHT,
+ CervicalMucusRecord.SENSATION_MEDIUM to PlatformCervicalMucusSensation.SENSATION_MEDIUM,
+ CervicalMucusRecord.SENSATION_HEAVY to PlatformCervicalMucusSensation.SENSATION_HEAVY,
+ )
+
+internal val PLATFORM_TO_SDK_CERVICAL_MUCUS_SENSATION =
+ SDK_TO_PLATFORM_CERVICAL_MUCUS_SENSATION.reversed()
+
+internal val SDK_TO_PLATFORM_SEXUAL_ACTIVITY_PROTECTION_USED: Map<Int, Int> =
+ mapOf(
+ SexualActivityRecord.PROTECTION_USED_PROTECTED to
+ PlatformSexualActivityProtectionUsed.PROTECTION_USED_PROTECTED,
+ SexualActivityRecord.PROTECTION_USED_UNPROTECTED to
+ PlatformSexualActivityProtectionUsed.PROTECTION_USED_UNPROTECTED,
+ )
+
+internal val PLATFORM_TO_SDK_SEXUAL_ACTIVITY_PROTECTION_USED =
+ SDK_TO_PLATFORM_SEXUAL_ACTIVITY_PROTECTION_USED.reversed()
+
+internal val SDK_TO_PLATFORM_BLOOD_GLUCOSE_SPECIMEN_SOURCE: Map<Int, Int> =
+ mapOf(
+ BloodGlucoseRecord.SPECIMEN_SOURCE_INTERSTITIAL_FLUID to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_INTERSTITIAL_FLUID,
+ BloodGlucoseRecord.SPECIMEN_SOURCE_CAPILLARY_BLOOD to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_CAPILLARY_BLOOD,
+ BloodGlucoseRecord.SPECIMEN_SOURCE_PLASMA to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_PLASMA,
+ BloodGlucoseRecord.SPECIMEN_SOURCE_SERUM to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_SERUM,
+ BloodGlucoseRecord.SPECIMEN_SOURCE_TEARS to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_TEARS,
+ BloodGlucoseRecord.SPECIMEN_SOURCE_WHOLE_BLOOD to
+ PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_WHOLE_BLOOD,
+ )
+
+internal val PLATFORM_TO_SDK_GLUCOSE_SPECIMEN_SOURCE =
+ SDK_TO_PLATFORM_BLOOD_GLUCOSE_SPECIMEN_SOURCE.reversed()
+
+internal val SDK_TO_PLATFORM_BLOOD_GLUCOSE_RELATION_TO_MEAL: Map<Int, Int> =
+ mapOf(
+ BloodGlucoseRecord.RELATION_TO_MEAL_GENERAL to
+ PlatformBloodGlucoseRelationToMeal.RELATION_TO_MEAL_GENERAL,
+ BloodGlucoseRecord.RELATION_TO_MEAL_FASTING to
+ PlatformBloodGlucoseRelationToMeal.RELATION_TO_MEAL_FASTING,
+ BloodGlucoseRecord.RELATION_TO_MEAL_BEFORE_MEAL to
+ PlatformBloodGlucoseRelationToMeal.RELATION_TO_MEAL_BEFORE_MEAL,
+ BloodGlucoseRecord.RELATION_TO_MEAL_AFTER_MEAL to
+ PlatformBloodGlucoseRelationToMeal.RELATION_TO_MEAL_AFTER_MEAL,
+ )
+
+internal val PLATFORM_TO_SDK_BLOOD_GLUCOSE_RELATION_TO_MEAL =
+ SDK_TO_PLATFORM_BLOOD_GLUCOSE_RELATION_TO_MEAL.reversed()
+
+internal val SDK_TO_PLATFORM_SLEEP_STAGE_TYPE: Map<Int, Int> =
+ mapOf(
+ SleepSessionRecord.STAGE_TYPE_AWAKE to PlatformSleepStageType.STAGE_TYPE_AWAKE,
+ SleepSessionRecord.STAGE_TYPE_SLEEPING to PlatformSleepStageType.STAGE_TYPE_SLEEPING,
+ SleepSessionRecord.STAGE_TYPE_OUT_OF_BED to
+ PlatformSleepStageType.STAGE_TYPE_AWAKE_OUT_OF_BED,
+ SleepSessionRecord.STAGE_TYPE_LIGHT to PlatformSleepStageType.STAGE_TYPE_SLEEPING_LIGHT,
+ SleepSessionRecord.STAGE_TYPE_DEEP to PlatformSleepStageType.STAGE_TYPE_SLEEPING_DEEP,
+ SleepSessionRecord.STAGE_TYPE_REM to PlatformSleepStageType.STAGE_TYPE_SLEEPING_REM,
+ SleepSessionRecord.STAGE_TYPE_AWAKE_IN_BED to PlatformSleepStageType.STAGE_TYPE_AWAKE_IN_BED
+ )
+
+internal val PLATFORM_TO_SDK_SLEEP_STAGE_TYPE = SDK_TO_PLATFORM_SLEEP_STAGE_TYPE.reversed()
+
+internal val SDK_TO_PLATFORM_EXERCISE_SEGMENT_TYPE: Map<Int, Int> =
+ mapOf(
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_ARM_CURL to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ARM_CURL,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BACK_EXTENSION to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BACK_EXTENSION,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BALL_SLAM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BALL_SLAM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BARBELL_SHOULDER_PRESS,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BENCH_PRESS to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BENCH_PRESS,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BENCH_SIT_UP,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BIKING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_BURPEE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BURPEE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_CRUNCH to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_CRUNCH,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DEADLIFT to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DEADLIFT,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DOUBLE_ARM_TRICEPS_EXTENSION,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_LEFT_ARM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_CURL_RIGHT_ARM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_FRONT_RAISE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_LATERAL_RAISE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_ROW,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_LEFT_ARM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_RIGHT_ARM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_DUMBBELL_TRICEPS_EXTENSION_TWO_ARM,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_ELLIPTICAL to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ELLIPTICAL,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_FORWARD_TWIST to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_FORWARD_TWIST,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_FRONT_RAISE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_FRONT_RAISE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_HIP_THRUST to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_HIP_THRUST,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_HULA_HOOP to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_HULA_HOOP,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_JUMPING_JACK to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_JUMPING_JACK,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_JUMP_ROPE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_JUMP_ROPE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_KETTLEBELL_SWING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LATERAL_RAISE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LATERAL_RAISE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LAT_PULL_DOWN,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LEG_CURL to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LEG_CURL,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LEG_EXTENSION to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LEG_EXTENSION,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LEG_PRESS to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LEG_PRESS,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LEG_RAISE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LEG_RAISE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_LUNGE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_LUNGE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_MOUNTAIN_CLIMBER,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_OTHER_WORKOUT,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_PAUSE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_PAUSE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_PILATES to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_PILATES,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_PLANK,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_PULL_UP to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_PULL_UP,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_PUNCH to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_PUNCH,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_REST to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_REST,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_ROWING_MACHINE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ROWING_MACHINE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING_TREADMILL,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SHOULDER_PRESS,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SINGLE_ARM_TRICEPS_EXTENSION,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SIT_UP to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SIT_UP,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SQUAT to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SQUAT,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_STAIR_CLIMBING_MACHINE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_STRETCHING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_STRETCHING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_BACKSTROKE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_BREASTSTROKE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_BUTTERFLY,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_FREESTYLE,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_MIXED,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_OPEN_WATER,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_OTHER,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_SWIMMING_POOL to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_SWIMMING_POOL,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_UPPER_TWIST to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_UPPER_TWIST,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_WALKING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_WALKING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_WEIGHTLIFTING,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_WHEELCHAIR to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_WHEELCHAIR,
+ ExerciseSegment.EXERCISE_SEGMENT_TYPE_YOGA to
+ PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_YOGA,
+ )
+
+internal val PLATFORM_TO_SDK_EXERCISE_SEGMENT_TYPE =
+ SDK_TO_PLATFORM_EXERCISE_SEGMENT_TYPE.reversed()
+
+internal val SDK_TO_PLATFORM_RECORDING_METHOD: Map<Int, Int> =
+ mapOf(
+ Metadata.RECORDING_METHOD_ACTIVELY_RECORDED to
+ PlatformMetadata.RECORDING_METHOD_ACTIVELY_RECORDED,
+ Metadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED to
+ PlatformMetadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED,
+ Metadata.RECORDING_METHOD_MANUAL_ENTRY to PlatformMetadata.RECORDING_METHOD_MANUAL_ENTRY
+ )
+
+internal val PLATFORM_TO_SDK_RECORDING_METHOD: Map<Int, Int> =
+ SDK_TO_PLATFORM_RECORDING_METHOD.reversed()
+
+internal fun Int.toPlatformCervicalMucusAppearance(): Int {
+ return SDK_TO_PLATFORM_CERVICAL_MUCUS_APPEARANCE[this]
+ ?: PlatformCervicalMucusAppearance.APPEARANCE_UNKNOWN
+}
+
+internal fun Int.toPlatformBloodPressureBodyPosition(): Int {
+ return SDK_TO_PLATFORM_BLOOD_PRESSURE_BODY_POSITION[this]
+ ?: PlatformBloodPressureBodyPosition.BODY_POSITION_UNKNOWN
+}
+
+internal fun Int.toPlatformExerciseSessionType(): Int {
+ return SDK_TO_PLATFORM_EXERCISE_SESSION_TYPE[this]
+ ?: PlatformExerciseSessionType.EXERCISE_SESSION_TYPE_UNKNOWN
+}
+
+internal fun Int.toPlatformExerciseSegmentType(): Int {
+ return SDK_TO_PLATFORM_EXERCISE_SEGMENT_TYPE[this]
+ ?: PlatformExerciseSegmentType.EXERCISE_SEGMENT_TYPE_UNKNOWN
+}
+
+internal fun Int.toPlatformMealType(): Int {
+ return SDK_TO_PLATFORM_MEAL_TYPE[this] ?: PlatformMealType.MEAL_TYPE_UNKNOWN
+}
+
+internal fun Int.toPlatformVo2MaxMeasurementMethod(): Int {
+ return SDK_TO_PLATFORM_VO2_MAX_MEASUREMENT_METHOD[this]
+ ?: PlatformVo2MaxMeasurementMethod.MEASUREMENT_METHOD_OTHER
+}
+
+internal fun Int.toPlatformMenstruationFlow(): Int {
+ return SDK_TO_PLATFORM_MENSTRUATION_FLOW_TYPE[this] ?: PlatformMenstruationFlowType.FLOW_UNKNOWN
+}
+
+internal fun Int.toPlatformBodyTemperatureMeasurementLocation(): Int {
+ return SDK_TO_PLATFORM_BODY_TEMPERATURE_MEASUREMENT_LOCATION[this]
+ ?: PlatformBodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_UNKNOWN
+}
+
+internal fun Int.toPlatformBloodPressureMeasurementLocation(): Int {
+ return SDK_TO_PLATFORM_BLOOD_PRESSURE_MEASUREMENT_LOCATION[this]
+ ?: PlatformBloodPressureMeasurementLocation.BLOOD_PRESSURE_MEASUREMENT_LOCATION_UNKNOWN
+}
+
+internal fun Int.toPlatformOvulationTestResult(): Int {
+ return SDK_TO_PLATFORM_OVULATION_TEST_RESULT[this]
+ ?: PlatformOvulationTestResult.RESULT_INCONCLUSIVE
+}
+
+internal fun Int.toPlatformCervicalMucusSensation(): Int {
+ return SDK_TO_PLATFORM_CERVICAL_MUCUS_SENSATION[this]
+ ?: PlatformCervicalMucusSensation.SENSATION_UNKNOWN
+}
+
+internal fun Int.toPlatformSexualActivityProtectionUsed(): Int {
+ return SDK_TO_PLATFORM_SEXUAL_ACTIVITY_PROTECTION_USED[this]
+ ?: PlatformSexualActivityProtectionUsed.PROTECTION_USED_UNKNOWN
+}
+
+internal fun Int.toPlatformBloodGlucoseSpecimenSource(): Int {
+ return SDK_TO_PLATFORM_BLOOD_GLUCOSE_SPECIMEN_SOURCE[this]
+ ?: PlatformBloodGlucoseSpecimenSource.SPECIMEN_SOURCE_UNKNOWN
+}
+
+internal fun Int.toPlatformBloodGlucoseRelationToMeal(): Int {
+ return SDK_TO_PLATFORM_BLOOD_GLUCOSE_RELATION_TO_MEAL[this]
+ ?: PlatformBloodGlucoseRelationToMeal.RELATION_TO_MEAL_UNKNOWN
+}
+
+internal fun Int.toPlatformSleepStageType(): Int {
+ return SDK_TO_PLATFORM_SLEEP_STAGE_TYPE[this] ?: PlatformSleepStageType.STAGE_TYPE_UNKNOWN
+}
+
+internal fun Int.toPlatformRecordingMethod(): Int {
+ return SDK_TO_PLATFORM_RECORDING_METHOD[this] ?: PlatformMetadata.RECORDING_METHOD_UNKNOWN
+}
+
+internal fun Int.toSdkBloodPressureBodyPosition(): Int {
+ return PLATFORM_TO_SDK_BLOOD_PRESSURE_BODY_POSITION[this]
+ ?: BloodPressureRecord.BODY_POSITION_UNKNOWN
+}
+
+internal fun Int.toSdkBloodPressureMeasurementLocation(): Int {
+ return PLATFORM_TO_SDK_BLOOD_PRESSURE_MEASUREMENT_LOCATION[this]
+ ?: BloodPressureRecord.MEASUREMENT_LOCATION_UNKNOWN
+}
+
+internal fun Int.toSdkExerciseSessionType(): Int {
+ return PLATFORM_TO_SDK_EXERCISE_SESSION_TYPE[this]
+ ?: ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT
+}
+
+internal fun Int.toSdkExerciseSegmentType(): Int {
+ return PLATFORM_TO_SDK_EXERCISE_SEGMENT_TYPE[this]
+ ?: ExerciseSegment.EXERCISE_SEGMENT_TYPE_UNKNOWN
+}
+
+internal fun Int.toSdkVo2MaxMeasurementMethod(): Int {
+ return PLATFORM_TO_SDK_VO2_MAX_MEASUREMENT_METHOD[this] ?: Vo2MaxRecord.MEASUREMENT_METHOD_OTHER
+}
+
+internal fun Int.toSdkMenstruationFlow(): Int {
+ return PLATFORM_TO_SDK_MENSTRUATION_FLOW_TYPE[this] ?: MenstruationFlowRecord.FLOW_UNKNOWN
+}
+
+internal fun Int.toSdkProtectionUsed(): Int {
+ return PLATFORM_TO_SDK_SEXUAL_ACTIVITY_PROTECTION_USED[this]
+ ?: SexualActivityRecord.PROTECTION_USED_UNKNOWN
+}
+
+internal fun Int.toSdkCervicalMucusSensation(): Int {
+ return PLATFORM_TO_SDK_CERVICAL_MUCUS_SENSATION[this] ?: CervicalMucusRecord.SENSATION_UNKNOWN
+}
+
+internal fun Int.toSdkBloodGlucoseSpecimenSource(): Int {
+ return PLATFORM_TO_SDK_GLUCOSE_SPECIMEN_SOURCE[this]
+ ?: BloodGlucoseRecord.SPECIMEN_SOURCE_UNKNOWN
+}
+
+internal fun Int.toSdkMealType(): Int {
+ return PLATFORM_TO_SDK_MEAL_TYPE[this] ?: MealType.MEAL_TYPE_UNKNOWN
+}
+
+internal fun Int.toSdkOvulationTestResult(): Int {
+ return PLATFORM_TO_SDK_OVULATION_TEST_RESULT[this] ?: OvulationTestRecord.RESULT_INCONCLUSIVE
+}
+
+internal fun Int.toSdkRelationToMeal(): Int {
+ return PLATFORM_TO_SDK_BLOOD_GLUCOSE_RELATION_TO_MEAL[this]
+ ?: BloodGlucoseRecord.RELATION_TO_MEAL_UNKNOWN
+}
+
+internal fun Int.toSdkBodyTemperatureMeasurementLocation(): Int {
+ return PLATFORM_TO_SDK_BODY_TEMPERATURE_MEASUREMENT_LOCATION[this]
+ ?: BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_UNKNOWN
+}
+
+internal fun Int.toSdkCervicalMucusAppearance(): Int {
+ return PLATFORM_TO_SDK_CERVICAL_MUCUS_APPEARANCE[this] ?: CervicalMucusRecord.APPEARANCE_UNKNOWN
+}
+
+internal fun Int.toSdkSleepStageType(): Int {
+ return PLATFORM_TO_SDK_SLEEP_STAGE_TYPE[this] ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
+}
+
+internal fun Int.toSdkRecordingMethod(): Int {
+ return PLATFORM_TO_SDK_RECORDING_METHOD[this] ?: Metadata.RECORDING_METHOD_UNKNOWN
+}
+
+private fun Map<Int, Int>.reversed(): Map<Int, Int> {
+ return entries.associate { (k, v) -> v to k }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/MetadataConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/MetadataConverters.kt
new file mode 100644
index 0000000..f4f74d1
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/MetadataConverters.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.records.metadata.DataOrigin
+import androidx.health.connect.client.records.metadata.Device
+import androidx.health.connect.client.records.metadata.Metadata
+
+internal fun PlatformMetadata.toSdkMetadata(): Metadata {
+ return Metadata(
+ id = id,
+ dataOrigin = dataOrigin.toSdkDataOrigin(),
+ lastModifiedTime = lastModifiedTime,
+ clientRecordId = clientRecordId,
+ clientRecordVersion = clientRecordVersion,
+ recordingMethod = recordingMethod.toSdkRecordingMethod(),
+ device = device.toSdkDevice()
+ )
+}
+
+internal fun PlatformDevice.toSdkDevice(): Device {
+ @Suppress("WrongConstant") // Platform intdef and jetpack intdef match in value.
+ return Device(manufacturer = manufacturer, model = model, type = type)
+}
+
+internal fun PlatformDataOrigin.toSdkDataOrigin(): DataOrigin {
+ return DataOrigin(packageName)
+}
+
+internal fun Metadata.toPlatformMetadata(): PlatformMetadata {
+ return PlatformMetadataBuilder()
+ .apply {
+ device?.toPlatformDevice()?.let { setDevice(it) }
+ setLastModifiedTime(lastModifiedTime)
+ setId(id)
+ setDataOrigin(dataOrigin.toPlatformDataOrigin())
+ setClientRecordId(clientRecordId)
+ setClientRecordVersion(clientRecordVersion)
+ setRecordingMethod(recordingMethod.toPlatformRecordingMethod())
+ }
+ .build()
+}
+
+internal fun DataOrigin.toPlatformDataOrigin(): PlatformDataOrigin {
+ return PlatformDataOriginBuilder().apply { setPackageName(packageName) }.build()
+}
+
+internal fun Device.toPlatformDevice(): PlatformDevice {
+ @Suppress("WrongConstant") // Platform intdef and jetpack intdef match in value.
+ return PlatformDeviceBuilder()
+ .apply {
+ setType(type)
+ manufacturer?.let { setManufacturer(it) }
+ model?.let { setModel(it) }
+ }
+ .build()
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/PlatformRecordAliases.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/PlatformRecordAliases.kt
new file mode 100644
index 0000000..b1653a2
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/PlatformRecordAliases.kt
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+
+internal typealias PlatformInstantRecord = android.health.connect.datatypes.InstantRecord
+
+internal typealias PlatformIntervalRecord = android.health.connect.datatypes.IntervalRecord
+
+internal typealias PlatformRecord = android.health.connect.datatypes.Record
+
+internal typealias PlatformActiveCaloriesBurnedRecord =
+ android.health.connect.datatypes.ActiveCaloriesBurnedRecord
+
+internal typealias PlatformActiveCaloriesBurnedRecordBuilder =
+ android.health.connect.datatypes.ActiveCaloriesBurnedRecord.Builder
+
+internal typealias PlatformBasalBodyTemperatureRecord =
+ android.health.connect.datatypes.BasalBodyTemperatureRecord
+
+internal typealias PlatformBasalBodyTemperatureRecordBuilder =
+ android.health.connect.datatypes.BasalBodyTemperatureRecord.Builder
+
+internal typealias PlatformBodyTemperatureMeasurementLocation =
+ android.health.connect.datatypes.BodyTemperatureMeasurementLocation
+
+internal typealias PlatformBasalMetabolicRateRecord =
+ android.health.connect.datatypes.BasalMetabolicRateRecord
+
+internal typealias PlatformBasalMetabolicRateRecordBuilder =
+ android.health.connect.datatypes.BasalMetabolicRateRecord.Builder
+
+internal typealias PlatformBloodGlucoseRecord = android.health.connect.datatypes.BloodGlucoseRecord
+
+internal typealias PlatformBloodGlucoseRecordBuilder =
+ android.health.connect.datatypes.BloodGlucoseRecord.Builder
+
+internal typealias PlatformBloodGlucoseSpecimenSource =
+ android.health.connect.datatypes.BloodGlucoseRecord.SpecimenSource
+
+internal typealias PlatformBloodGlucoseRelationToMealType =
+ android.health.connect.datatypes.BloodGlucoseRecord.RelationToMealType
+
+internal typealias PlatformBloodPressureRecord =
+ android.health.connect.datatypes.BloodPressureRecord
+
+internal typealias PlatformBloodPressureRecordBuilder =
+ android.health.connect.datatypes.BloodPressureRecord.Builder
+
+internal typealias PlatformBloodGlucoseRelationToMeal =
+ android.health.connect.datatypes.BloodGlucoseRecord.RelationToMealType
+
+internal typealias PlatformBloodPressureBodyPosition =
+ android.health.connect.datatypes.BloodPressureRecord.BodyPosition
+
+internal typealias PlatformBloodPressureMeasurementLocation =
+ android.health.connect.datatypes.BloodPressureRecord.BloodPressureMeasurementLocation
+
+internal typealias PlatformBodyFatRecord = android.health.connect.datatypes.BodyFatRecord
+
+internal typealias PlatformBodyFatRecordBuilder =
+ android.health.connect.datatypes.BodyFatRecord.Builder
+
+internal typealias PlatformBodyTemperatureRecord =
+ android.health.connect.datatypes.BodyTemperatureRecord
+
+internal typealias PlatformBodyTemperatureRecordBuilder =
+ android.health.connect.datatypes.BodyTemperatureRecord.Builder
+
+internal typealias PlatformBodyWaterMassRecord =
+ android.health.connect.datatypes.BodyWaterMassRecord
+
+internal typealias PlatformBodyWaterMassRecordBuilder =
+ android.health.connect.datatypes.BodyWaterMassRecord.Builder
+
+internal typealias PlatformBoneMassRecord = android.health.connect.datatypes.BoneMassRecord
+
+internal typealias PlatformBoneMassRecordBuilder =
+ android.health.connect.datatypes.BoneMassRecord.Builder
+
+internal typealias PlatformCervicalMucusRecord =
+ android.health.connect.datatypes.CervicalMucusRecord
+
+internal typealias PlatformCervicalMucusRecordBuilder =
+ android.health.connect.datatypes.CervicalMucusRecord.Builder
+
+internal typealias PlatformCervicalMucusAppearance =
+ android.health.connect.datatypes.CervicalMucusRecord.CervicalMucusAppearance
+
+internal typealias PlatformCervicalMucusSensation =
+ android.health.connect.datatypes.CervicalMucusRecord.CervicalMucusSensation
+
+internal typealias PlatformCyclingPedalingCadenceRecord =
+ android.health.connect.datatypes.CyclingPedalingCadenceRecord
+
+internal typealias PlatformCyclingPedalingCadenceRecordBuilder =
+ android.health.connect.datatypes.CyclingPedalingCadenceRecord.Builder
+
+internal typealias PlatformCyclingPedalingCadenceSample =
+ android.health.connect.datatypes.CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample
+
+internal typealias PlatformDistanceRecord = android.health.connect.datatypes.DistanceRecord
+
+internal typealias PlatformDistanceRecordBuilder =
+ android.health.connect.datatypes.DistanceRecord.Builder
+
+internal typealias PlatformElevationGainedRecord =
+ android.health.connect.datatypes.ElevationGainedRecord
+
+internal typealias PlatformElevationGainedRecordBuilder =
+ android.health.connect.datatypes.ElevationGainedRecord.Builder
+
+internal typealias PlatformExerciseLap = android.health.connect.datatypes.ExerciseLap
+
+internal typealias PlatformExerciseLapBuilder = android.health.connect.datatypes.ExerciseLap.Builder
+
+internal typealias PlatformExerciseSegment = android.health.connect.datatypes.ExerciseSegment
+
+internal typealias PlatformExerciseSegmentBuilder =
+ android.health.connect.datatypes.ExerciseSegment.Builder
+
+internal typealias PlatformExerciseSegmentType =
+ android.health.connect.datatypes.ExerciseSegmentType
+
+internal typealias PlatformExerciseSessionRecord =
+ android.health.connect.datatypes.ExerciseSessionRecord
+
+internal typealias PlatformExerciseSessionRecordBuilder =
+ android.health.connect.datatypes.ExerciseSessionRecord.Builder
+
+internal typealias PlatformExerciseSessionType =
+ android.health.connect.datatypes.ExerciseSessionType
+
+internal typealias PlatformExerciseRoute = android.health.connect.datatypes.ExerciseRoute
+
+internal typealias PlatformExerciseRouteLocation =
+ android.health.connect.datatypes.ExerciseRoute.Location
+
+internal typealias PlatformExerciseRouteLocationBuilder =
+ android.health.connect.datatypes.ExerciseRoute.Location.Builder
+
+internal typealias PlatformFloorsClimbedRecord =
+ android.health.connect.datatypes.FloorsClimbedRecord
+
+internal typealias PlatformFloorsClimbedRecordBuilder =
+ android.health.connect.datatypes.FloorsClimbedRecord.Builder
+
+internal typealias PlatformHeartRateRecord = android.health.connect.datatypes.HeartRateRecord
+
+internal typealias PlatformHeartRateRecordBuilder =
+ android.health.connect.datatypes.HeartRateRecord.Builder
+
+internal typealias PlatformHeartRateSample =
+ android.health.connect.datatypes.HeartRateRecord.HeartRateSample
+
+internal typealias PlatformHeartRateVariabilityRmssdRecord =
+ android.health.connect.datatypes.HeartRateVariabilityRmssdRecord
+
+internal typealias PlatformHeartRateVariabilityRmssdRecordBuilder =
+ android.health.connect.datatypes.HeartRateVariabilityRmssdRecord.Builder
+
+internal typealias PlatformHeightRecord = android.health.connect.datatypes.HeightRecord
+
+internal typealias PlatformHeightRecordBuilder =
+ android.health.connect.datatypes.HeightRecord.Builder
+
+internal typealias PlatformHydrationRecord = android.health.connect.datatypes.HydrationRecord
+
+internal typealias PlatformHydrationRecordBuilder =
+ android.health.connect.datatypes.HydrationRecord.Builder
+
+internal typealias PlatformIntermenstrualBleedingRecord =
+ android.health.connect.datatypes.IntermenstrualBleedingRecord
+
+internal typealias PlatformIntermenstrualBleedingRecordBuilder =
+ android.health.connect.datatypes.IntermenstrualBleedingRecord.Builder
+
+internal typealias PlatformLeanBodyMassRecord = android.health.connect.datatypes.LeanBodyMassRecord
+
+internal typealias PlatformLeanBodyMassRecordBuilder =
+ android.health.connect.datatypes.LeanBodyMassRecord.Builder
+
+internal typealias PlatformMenstruationFlowRecord =
+ android.health.connect.datatypes.MenstruationFlowRecord
+
+internal typealias PlatformMenstruationFlowRecordBuilder =
+ android.health.connect.datatypes.MenstruationFlowRecord.Builder
+
+internal typealias PlatformMenstruationFlowType =
+ android.health.connect.datatypes.MenstruationFlowRecord.MenstruationFlowType
+
+internal typealias PlatformMealType = android.health.connect.datatypes.MealType
+
+internal typealias PlatformMenstruationPeriodRecord =
+ android.health.connect.datatypes.MenstruationPeriodRecord
+
+internal typealias PlatformMenstruationPeriodRecordBuilder =
+ android.health.connect.datatypes.MenstruationPeriodRecord.Builder
+
+internal typealias PlatformNutritionRecord = android.health.connect.datatypes.NutritionRecord
+
+internal typealias PlatformNutritionRecordBuilder =
+ android.health.connect.datatypes.NutritionRecord.Builder
+
+internal typealias PlatformOvulationTestRecord =
+ android.health.connect.datatypes.OvulationTestRecord
+
+internal typealias PlatformOvulationTestRecordBuilder =
+ android.health.connect.datatypes.OvulationTestRecord.Builder
+
+internal typealias PlatformOvulationTestResult =
+ android.health.connect.datatypes.OvulationTestRecord.OvulationTestResult
+
+internal typealias PlatformOxygenSaturationRecord =
+ android.health.connect.datatypes.OxygenSaturationRecord
+
+internal typealias PlatformOxygenSaturationRecordBuilder =
+ android.health.connect.datatypes.OxygenSaturationRecord.Builder
+
+internal typealias PlatformPowerRecord = android.health.connect.datatypes.PowerRecord
+
+internal typealias PlatformPowerRecordBuilder = android.health.connect.datatypes.PowerRecord.Builder
+
+internal typealias PlatformPowerRecordSample =
+ android.health.connect.datatypes.PowerRecord.PowerRecordSample
+
+internal typealias PlatformRespiratoryRateRecord =
+ android.health.connect.datatypes.RespiratoryRateRecord
+
+internal typealias PlatformRespiratoryRateRecordBuilder =
+ android.health.connect.datatypes.RespiratoryRateRecord.Builder
+
+internal typealias PlatformRestingHeartRateRecord =
+ android.health.connect.datatypes.RestingHeartRateRecord
+
+internal typealias PlatformRestingHeartRateRecordBuilder =
+ android.health.connect.datatypes.RestingHeartRateRecord.Builder
+
+internal typealias PlatformSexualActivityRecord =
+ android.health.connect.datatypes.SexualActivityRecord
+
+internal typealias PlatformSexualActivityRecordBuilder =
+ android.health.connect.datatypes.SexualActivityRecord.Builder
+
+internal typealias PlatformSexualActivityProtectionUsed =
+ android.health.connect.datatypes.SexualActivityRecord.SexualActivityProtectionUsed
+
+internal typealias PlatformSleepSessionRecord = android.health.connect.datatypes.SleepSessionRecord
+
+internal typealias PlatformSleepSessionRecordBuilder =
+ android.health.connect.datatypes.SleepSessionRecord.Builder
+
+internal typealias PlatformSleepSessionStage =
+ android.health.connect.datatypes.SleepSessionRecord.Stage
+
+internal typealias PlatformSleepStageType =
+ android.health.connect.datatypes.SleepSessionRecord.StageType
+
+internal typealias PlatformSpeedRecord = android.health.connect.datatypes.SpeedRecord
+
+internal typealias PlatformSpeedRecordBuilder = android.health.connect.datatypes.SpeedRecord.Builder
+
+internal typealias PlatformSpeedSample =
+ android.health.connect.datatypes.SpeedRecord.SpeedRecordSample
+
+internal typealias PlatformStepsCadenceRecord = android.health.connect.datatypes.StepsCadenceRecord
+
+internal typealias PlatformStepsCadenceRecordBuilder =
+ android.health.connect.datatypes.StepsCadenceRecord.Builder
+
+internal typealias PlatformStepsCadenceSample =
+ android.health.connect.datatypes.StepsCadenceRecord.StepsCadenceRecordSample
+
+internal typealias PlatformStepsRecord = android.health.connect.datatypes.StepsRecord
+
+internal typealias PlatformStepsRecordBuilder = android.health.connect.datatypes.StepsRecord.Builder
+
+internal typealias PlatformTotalCaloriesBurnedRecord =
+ android.health.connect.datatypes.TotalCaloriesBurnedRecord
+
+internal typealias PlatformTotalCaloriesBurnedRecordBuilder =
+ android.health.connect.datatypes.TotalCaloriesBurnedRecord.Builder
+
+internal typealias PlatformVo2MaxRecord = android.health.connect.datatypes.Vo2MaxRecord
+
+internal typealias PlatformVo2MaxRecordBuilder =
+ android.health.connect.datatypes.Vo2MaxRecord.Builder
+
+internal typealias PlatformVo2MaxMeasurementMethod =
+ android.health.connect.datatypes.Vo2MaxRecord.Vo2MaxMeasurementMethod
+
+internal typealias PlatformWeightRecord = android.health.connect.datatypes.WeightRecord
+
+internal typealias PlatformWeightRecordBuilder =
+ android.health.connect.datatypes.WeightRecord.Builder
+
+internal typealias PlatformWheelchairPushesRecord =
+ android.health.connect.datatypes.WheelchairPushesRecord
+
+internal typealias PlatformWheelchairPushesRecordBuilder =
+ android.health.connect.datatypes.WheelchairPushesRecord.Builder
+
+internal typealias PlatformDataOrigin = android.health.connect.datatypes.DataOrigin
+
+internal typealias PlatformDataOriginBuilder = android.health.connect.datatypes.DataOrigin.Builder
+
+internal typealias PlatformDevice = android.health.connect.datatypes.Device
+
+internal typealias PlatformDeviceBuilder = android.health.connect.datatypes.Device.Builder
+
+internal typealias PlatformMetadata = android.health.connect.datatypes.Metadata
+
+internal typealias PlatformMetadataBuilder = android.health.connect.datatypes.Metadata.Builder
+
+internal typealias PlatformBloodGlucose = android.health.connect.datatypes.units.BloodGlucose
+
+internal typealias PlatformEnergy = android.health.connect.datatypes.units.Energy
+
+internal typealias PlatformLength = android.health.connect.datatypes.units.Length
+
+internal typealias PlatformMass = android.health.connect.datatypes.units.Mass
+
+internal typealias PlatformPercentage = android.health.connect.datatypes.units.Percentage
+
+internal typealias PlatformPower = android.health.connect.datatypes.units.Power
+
+internal typealias PlatformPressure = android.health.connect.datatypes.units.Pressure
+
+internal typealias PlatformTemperature = android.health.connect.datatypes.units.Temperature
+
+internal typealias PlatformVelocity = android.health.connect.datatypes.units.Velocity
+
+internal typealias PlatformVolume = android.health.connect.datatypes.units.Volume
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
new file mode 100644
index 0000000..e82005f
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
@@ -0,0 +1,1052 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
+import androidx.health.connect.client.records.BasalBodyTemperatureRecord
+import androidx.health.connect.client.records.BasalMetabolicRateRecord
+import androidx.health.connect.client.records.BloodGlucoseRecord
+import androidx.health.connect.client.records.BloodPressureRecord
+import androidx.health.connect.client.records.BodyFatRecord
+import androidx.health.connect.client.records.BodyTemperatureRecord
+import androidx.health.connect.client.records.BodyWaterMassRecord
+import androidx.health.connect.client.records.BoneMassRecord
+import androidx.health.connect.client.records.CervicalMucusRecord
+import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.ElevationGainedRecord
+import androidx.health.connect.client.records.ExerciseLap
+import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseSegment
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.FloorsClimbedRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
+import androidx.health.connect.client.records.HeightRecord
+import androidx.health.connect.client.records.HydrationRecord
+import androidx.health.connect.client.records.IntermenstrualBleedingRecord
+import androidx.health.connect.client.records.LeanBodyMassRecord
+import androidx.health.connect.client.records.MenstruationFlowRecord
+import androidx.health.connect.client.records.MenstruationPeriodRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.OvulationTestRecord
+import androidx.health.connect.client.records.OxygenSaturationRecord
+import androidx.health.connect.client.records.PowerRecord
+import androidx.health.connect.client.records.Record
+import androidx.health.connect.client.records.RespiratoryRateRecord
+import androidx.health.connect.client.records.RestingHeartRateRecord
+import androidx.health.connect.client.records.SexualActivityRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.SpeedRecord
+import androidx.health.connect.client.records.StepsCadenceRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
+import androidx.health.connect.client.records.Vo2MaxRecord
+import androidx.health.connect.client.records.WeightRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import kotlin.reflect.KClass
+
+// TODO(b/270559291): Validate that all class fields are being converted.
+
+fun KClass<out Record>.toPlatformRecordClass(): Class<out PlatformRecord> {
+ return SDK_TO_PLATFORM_RECORD_CLASS[this]
+ ?: throw IllegalArgumentException("Unsupported record type $this")
+}
+
+fun Record.toPlatformRecord(): PlatformRecord {
+ return when (this) {
+ is ActiveCaloriesBurnedRecord -> toPlatformActiveCaloriesBurnedRecord()
+ is BasalBodyTemperatureRecord -> toPlatformBasalBodyTemperatureRecord()
+ is BasalMetabolicRateRecord -> toPlatformBasalMetabolicRateRecord()
+ is BloodGlucoseRecord -> toPlatformBloodGlucoseRecord()
+ is BloodPressureRecord -> toPlatformBloodPressureRecord()
+ is BodyFatRecord -> toPlatformBodyFatRecord()
+ is BodyTemperatureRecord -> toPlatformBodyTemperatureRecord()
+ is BodyWaterMassRecord -> toPlatformBodyWaterMassRecord()
+ is BoneMassRecord -> toPlatformBoneMassRecord()
+ is CervicalMucusRecord -> toPlatformCervicalMucusRecord()
+ is CyclingPedalingCadenceRecord -> toPlatformCyclingPedalingCadenceRecord()
+ is DistanceRecord -> toPlatformDistanceRecord()
+ is ElevationGainedRecord -> toPlatformElevationGainedRecord()
+ is ExerciseSessionRecord -> toPlatformExerciseSessionRecord()
+ is FloorsClimbedRecord -> toPlatformFloorsClimbedRecord()
+ is HeartRateRecord -> toPlatformHeartRateRecord()
+ is HeartRateVariabilityRmssdRecord -> toPlatformHeartRateVariabilityRmssdRecord()
+ is HeightRecord -> toPlatformHeightRecord()
+ is HydrationRecord -> toPlatformHydrationRecord()
+ is IntermenstrualBleedingRecord -> toPlatformIntermenstrualBleedingRecord()
+ is LeanBodyMassRecord -> toPlatformLeanBodyMassRecord()
+ is MenstruationFlowRecord -> toPlatformMenstruationFlowRecord()
+ is MenstruationPeriodRecord -> toPlatformMenstruationPeriodRecord()
+ is NutritionRecord -> toPlatformNutritionRecord()
+ is OvulationTestRecord -> toPlatformOvulationTestRecord()
+ is OxygenSaturationRecord -> toPlatformOxygenSaturationRecord()
+ is PowerRecord -> toPlatformPowerRecord()
+ is RespiratoryRateRecord -> toPlatformRespiratoryRateRecord()
+ is RestingHeartRateRecord -> toPlatformRestingHeartRateRecord()
+ is SexualActivityRecord -> toPlatformSexualActivityRecord()
+ is SleepSessionRecord -> toPlatformSleepSessionRecord()
+ is SpeedRecord -> toPlatformSpeedRecord()
+ is StepsCadenceRecord -> toPlatformStepsCadenceRecord()
+ is StepsRecord -> toPlatformStepsRecord()
+ is TotalCaloriesBurnedRecord -> toPlatformTotalCaloriesBurnedRecord()
+ is Vo2MaxRecord -> toPlatformVo2MaxRecord()
+ is WeightRecord -> toPlatformWeightRecord()
+ is WheelchairPushesRecord -> toPlatformWheelchairPushesRecord()
+ else -> throw IllegalArgumentException("Unsupported record $this")
+ }
+}
+
+fun PlatformRecord.toSdkRecord(): Record {
+ return when (this) {
+ is PlatformActiveCaloriesBurnedRecord -> toSdkActiveCaloriesBurnedRecord()
+ is PlatformBasalBodyTemperatureRecord -> toSdkBasalBodyTemperatureRecord()
+ is PlatformBasalMetabolicRateRecord -> toSdkBasalMetabolicRateRecord()
+ is PlatformBloodGlucoseRecord -> toSdkBloodGlucoseRecord()
+ is PlatformBloodPressureRecord -> toSdkBloodPressureRecord()
+ is PlatformBodyFatRecord -> toSdkBodyFatRecord()
+ is PlatformBodyTemperatureRecord -> toSdkBodyTemperatureRecord()
+ is PlatformBodyWaterMassRecord -> toSdkBodyWaterMassRecord()
+ is PlatformBoneMassRecord -> toSdkBoneMassRecord()
+ is PlatformCervicalMucusRecord -> toSdkCervicalMucusRecord()
+ is PlatformCyclingPedalingCadenceRecord -> toSdkCyclingPedalingCadenceRecord()
+ is PlatformDistanceRecord -> toSdkDistanceRecord()
+ is PlatformElevationGainedRecord -> toSdkElevationGainedRecord()
+ is PlatformExerciseSessionRecord -> toSdkExerciseSessionRecord()
+ is PlatformFloorsClimbedRecord -> toSdkFloorsClimbedRecord()
+ is PlatformHeartRateRecord -> toSdkHeartRateRecord()
+ is PlatformHeartRateVariabilityRmssdRecord -> toSdkHeartRateVariabilityRmssdRecord()
+ is PlatformHeightRecord -> toSdkHeightRecord()
+ is PlatformHydrationRecord -> toSdkHydrationRecord()
+ is PlatformIntermenstrualBleedingRecord -> toSdkIntermenstrualBleedingRecord()
+ is PlatformLeanBodyMassRecord -> toSdkLeanBodyMassRecord()
+ is PlatformMenstruationFlowRecord -> toSdkMenstruationFlowRecord()
+ is PlatformMenstruationPeriodRecord -> toSdkMenstruationPeriodRecord()
+ is PlatformNutritionRecord -> toSdkNutritionRecord()
+ is PlatformOvulationTestRecord -> toSdkOvulationTestRecord()
+ is PlatformOxygenSaturationRecord -> toSdkOxygenSaturationRecord()
+ is PlatformPowerRecord -> toSdkPowerRecord()
+ is PlatformRespiratoryRateRecord -> toSdkRespiratoryRateRecord()
+ is PlatformRestingHeartRateRecord -> toSdkRestingHeartRateRecord()
+ is PlatformSexualActivityRecord -> toSdkSexualActivityRecord()
+ is PlatformSleepSessionRecord -> toSdkSleepSessionRecord()
+ is PlatformSpeedRecord -> toSdkSpeedRecord()
+ is PlatformStepsCadenceRecord -> toSdkStepsCadenceRecord()
+ is PlatformStepsRecord -> toSdkStepsRecord()
+ is PlatformTotalCaloriesBurnedRecord -> toSdkTotalCaloriesBurnedRecord()
+ is PlatformVo2MaxRecord -> toSdkVo2MaxRecord()
+ is PlatformWeightRecord -> toSdkWeightRecord()
+ is PlatformWheelchairPushesRecord -> toWheelchairPushesRecord()
+ else -> throw IllegalArgumentException("Unsupported record $this")
+ }
+}
+
+private fun PlatformActiveCaloriesBurnedRecord.toSdkActiveCaloriesBurnedRecord() =
+ ActiveCaloriesBurnedRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ energy = energy.toSdkEnergy(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBasalBodyTemperatureRecord.toSdkBasalBodyTemperatureRecord() =
+ BasalBodyTemperatureRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ temperature = temperature.toSdkTemperature(),
+ measurementLocation = measurementLocation,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBasalMetabolicRateRecord.toSdkBasalMetabolicRateRecord() =
+ BasalMetabolicRateRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ basalMetabolicRate = basalMetabolicRate.toSdkPower(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBloodGlucoseRecord.toSdkBloodGlucoseRecord() =
+ BloodGlucoseRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ level = level.toSdkBloodGlucose(),
+ specimenSource = specimenSource.toSdkBloodGlucoseSpecimenSource(),
+ mealType = mealType.toSdkMealType(),
+ relationToMeal = relationToMeal.toSdkRelationToMeal(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBloodPressureRecord.toSdkBloodPressureRecord() =
+ BloodPressureRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ systolic = systolic.toSdkPressure(),
+ diastolic = diastolic.toSdkPressure(),
+ bodyPosition = bodyPosition.toSdkBloodPressureBodyPosition(),
+ measurementLocation = measurementLocation.toSdkBloodPressureMeasurementLocation(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBodyFatRecord.toSdkBodyFatRecord() =
+ BodyFatRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ percentage = percentage.toSdkPercentage(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBodyTemperatureRecord.toSdkBodyTemperatureRecord() =
+ BodyTemperatureRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ temperature = temperature.toSdkTemperature(),
+ measurementLocation = measurementLocation.toSdkBodyTemperatureMeasurementLocation(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBodyWaterMassRecord.toSdkBodyWaterMassRecord() =
+ BodyWaterMassRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ mass = bodyWaterMass.toSdkMass(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformBoneMassRecord.toSdkBoneMassRecord() =
+ BoneMassRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ mass = mass.toSdkMass(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformCervicalMucusRecord.toSdkCervicalMucusRecord() =
+ CervicalMucusRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ appearance = appearance.toSdkCervicalMucusAppearance(),
+ sensation = sensation.toSdkCervicalMucusSensation(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformCyclingPedalingCadenceRecord.toSdkCyclingPedalingCadenceRecord() =
+ CyclingPedalingCadenceRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ samples = samples.map { it.toSdkCyclingPedalingCadenceSample() }.sortedBy { it.time },
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformDistanceRecord.toSdkDistanceRecord() =
+ DistanceRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ distance = distance.toSdkLength(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformElevationGainedRecord.toSdkElevationGainedRecord() =
+ ElevationGainedRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ elevation = elevation.toSdkLength(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformExerciseSessionRecord.toSdkExerciseSessionRecord() =
+ ExerciseSessionRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ exerciseType = exerciseType.toSdkExerciseSessionType(),
+ title = title?.toString(),
+ notes = notes?.toString(),
+ route = route?.toSdkExerciseRoute(),
+ laps = laps.map { it.toSdkExerciseLap() }.sortedBy { it.startTime },
+ segments = segments.map { it.toSdkExerciseSegment() }.sortedBy { it.startTime },
+ hasRoute = hasRoute(),
+ metadata = metadata.toSdkMetadata(),
+ )
+
+private fun PlatformFloorsClimbedRecord.toSdkFloorsClimbedRecord() =
+ FloorsClimbedRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ floors = floors,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformHeartRateRecord.toSdkHeartRateRecord() =
+ HeartRateRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ samples = samples.map { it.toSdkHeartRateSample() }.sortedBy { it.time },
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformHeartRateVariabilityRmssdRecord.toSdkHeartRateVariabilityRmssdRecord() =
+ HeartRateVariabilityRmssdRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ heartRateVariabilityMillis = heartRateVariabilityMillis,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformHeightRecord.toSdkHeightRecord() =
+ HeightRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ height = height.toSdkLength(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformHydrationRecord.toSdkHydrationRecord() =
+ HydrationRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ volume = volume.toSdkVolume(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformIntermenstrualBleedingRecord.toSdkIntermenstrualBleedingRecord() =
+ IntermenstrualBleedingRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformLeanBodyMassRecord.toSdkLeanBodyMassRecord() =
+ LeanBodyMassRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ mass = mass.toSdkMass(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformMenstruationFlowRecord.toSdkMenstruationFlowRecord() =
+ MenstruationFlowRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ flow = flow.toSdkMenstruationFlow(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformMenstruationPeriodRecord.toSdkMenstruationPeriodRecord() =
+ MenstruationPeriodRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformNutritionRecord.toSdkNutritionRecord() =
+ NutritionRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ name = mealName,
+ mealType = mealType.toSdkMealType(),
+ metadata = metadata.toSdkMetadata(),
+ biotin = biotin?.toSdkMass(),
+ caffeine = caffeine?.toSdkMass(),
+ calcium = calcium?.toSdkMass(),
+ energy = energy?.toSdkEnergy(),
+ energyFromFat = energyFromFat?.toSdkEnergy(),
+ chloride = chloride?.toSdkMass(),
+ cholesterol = cholesterol?.toSdkMass(),
+ chromium = chromium?.toSdkMass(),
+ copper = copper?.toSdkMass(),
+ dietaryFiber = dietaryFiber?.toSdkMass(),
+ folate = folate?.toSdkMass(),
+ folicAcid = folicAcid?.toSdkMass(),
+ iodine = iodine?.toSdkMass(),
+ iron = iron?.toSdkMass(),
+ magnesium = magnesium?.toSdkMass(),
+ manganese = manganese?.toSdkMass(),
+ molybdenum = molybdenum?.toSdkMass(),
+ monounsaturatedFat = monounsaturatedFat?.toSdkMass(),
+ niacin = niacin?.toSdkMass(),
+ pantothenicAcid = pantothenicAcid?.toSdkMass(),
+ phosphorus = phosphorus?.toSdkMass(),
+ polyunsaturatedFat = polyunsaturatedFat?.toSdkMass(),
+ potassium = potassium?.toSdkMass(),
+ protein = protein?.toSdkMass(),
+ riboflavin = riboflavin?.toSdkMass(),
+ saturatedFat = saturatedFat?.toSdkMass(),
+ selenium = selenium?.toSdkMass(),
+ sodium = sodium?.toSdkMass(),
+ sugar = sugar?.toSdkMass(),
+ thiamin = thiamin?.toSdkMass(),
+ totalCarbohydrate = totalCarbohydrate?.toSdkMass(),
+ totalFat = totalFat?.toSdkMass(),
+ transFat = transFat?.toSdkMass(),
+ unsaturatedFat = unsaturatedFat?.toSdkMass(),
+ vitaminA = vitaminA?.toSdkMass(),
+ vitaminB12 = vitaminB12?.toSdkMass(),
+ vitaminB6 = vitaminB6?.toSdkMass(),
+ vitaminC = vitaminC?.toSdkMass(),
+ vitaminD = vitaminD?.toSdkMass(),
+ vitaminE = vitaminE?.toSdkMass(),
+ vitaminK = vitaminK?.toSdkMass(),
+ zinc = zinc?.toSdkMass()
+ )
+
+private fun PlatformOvulationTestRecord.toSdkOvulationTestRecord() =
+ OvulationTestRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ result = result.toSdkOvulationTestResult(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformOxygenSaturationRecord.toSdkOxygenSaturationRecord() =
+ OxygenSaturationRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ percentage = percentage.toSdkPercentage(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformPowerRecord.toSdkPowerRecord() =
+ PowerRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ samples = samples.map { it.toSdkPowerRecordSample() }.sortedBy { it.time },
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformRespiratoryRateRecord.toSdkRespiratoryRateRecord() =
+ RespiratoryRateRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ rate = rate,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformRestingHeartRateRecord.toSdkRestingHeartRateRecord() =
+ RestingHeartRateRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ beatsPerMinute = beatsPerMinute,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformSexualActivityRecord.toSdkSexualActivityRecord() =
+ SexualActivityRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ protectionUsed = protectionUsed.toSdkProtectionUsed(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformSleepSessionRecord.toSdkSleepSessionRecord() =
+ SleepSessionRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ metadata = metadata.toSdkMetadata(),
+ title = title?.toString(),
+ notes = notes?.toString(),
+ stages = stages.map { it.toSdkSleepSessionStage() }.sortedBy { it.startTime },
+ )
+
+private fun PlatformSpeedRecord.toSdkSpeedRecord() =
+ SpeedRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ samples = samples.map { it.toSdkSpeedSample() }.sortedBy { it.time },
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformStepsCadenceRecord.toSdkStepsCadenceRecord() =
+ StepsCadenceRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ samples = samples.map { it.toSdkStepsCadenceSample() }.sortedBy { it.time },
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformStepsRecord.toSdkStepsRecord() =
+ StepsRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ count = count,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformTotalCaloriesBurnedRecord.toSdkTotalCaloriesBurnedRecord() =
+ TotalCaloriesBurnedRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ energy = energy.toSdkEnergy(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformVo2MaxRecord.toSdkVo2MaxRecord() =
+ Vo2MaxRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ vo2MillilitersPerMinuteKilogram = vo2MillilitersPerMinuteKilogram,
+ measurementMethod = measurementMethod.toSdkVo2MaxMeasurementMethod(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformWeightRecord.toSdkWeightRecord() =
+ WeightRecord(
+ time = time,
+ zoneOffset = zoneOffset,
+ weight = weight.toSdkMass(),
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun PlatformWheelchairPushesRecord.toWheelchairPushesRecord() =
+ WheelchairPushesRecord(
+ startTime = startTime,
+ startZoneOffset = startZoneOffset,
+ endTime = endTime,
+ endZoneOffset = endZoneOffset,
+ count = count,
+ metadata = metadata.toSdkMetadata()
+ )
+
+private fun ActiveCaloriesBurnedRecord.toPlatformActiveCaloriesBurnedRecord() =
+ PlatformActiveCaloriesBurnedRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ energy.toPlatformEnergy(),
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun BasalBodyTemperatureRecord.toPlatformBasalBodyTemperatureRecord() =
+ PlatformBasalBodyTemperatureRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ measurementLocation.toPlatformBodyTemperatureMeasurementLocation(),
+ temperature.toPlatformTemperature()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BasalMetabolicRateRecord.toPlatformBasalMetabolicRateRecord() =
+ PlatformBasalMetabolicRateRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ basalMetabolicRate.toPlatformPower()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BloodGlucoseRecord.toPlatformBloodGlucoseRecord() =
+ PlatformBloodGlucoseRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ specimenSource.toPlatformBloodGlucoseSpecimenSource(),
+ level.toPlatformBloodGlucose(),
+ relationToMeal.toPlatformBloodGlucoseRelationToMeal(),
+ mealType.toPlatformMealType()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BloodPressureRecord.toPlatformBloodPressureRecord() =
+ PlatformBloodPressureRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ measurementLocation.toPlatformBloodPressureMeasurementLocation(),
+ systolic.toPlatformPressure(),
+ diastolic.toPlatformPressure(),
+ bodyPosition.toPlatformBloodPressureBodyPosition()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BodyFatRecord.toPlatformBodyFatRecord() =
+ PlatformBodyFatRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ percentage.toPlatformPercentage()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BodyTemperatureRecord.toPlatformBodyTemperatureRecord() =
+ PlatformBodyTemperatureRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ measurementLocation.toPlatformBodyTemperatureMeasurementLocation(),
+ temperature.toPlatformTemperature()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BodyWaterMassRecord.toPlatformBodyWaterMassRecord() =
+ PlatformBodyWaterMassRecordBuilder(metadata.toPlatformMetadata(), time, mass.toPlatformMass())
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun BoneMassRecord.toPlatformBoneMassRecord() =
+ PlatformBoneMassRecordBuilder(metadata.toPlatformMetadata(), time, mass.toPlatformMass())
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun CervicalMucusRecord.toPlatformCervicalMucusRecord() =
+ PlatformCervicalMucusRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ sensation.toPlatformCervicalMucusSensation(),
+ appearance.toPlatformCervicalMucusAppearance(),
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun CyclingPedalingCadenceRecord.toPlatformCyclingPedalingCadenceRecord() =
+ PlatformCyclingPedalingCadenceRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ samples.map { it.toPlatformCyclingPedalingCadenceSample() }
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun CyclingPedalingCadenceRecord.Sample.toPlatformCyclingPedalingCadenceSample() =
+ PlatformCyclingPedalingCadenceSample(revolutionsPerMinute, time)
+
+private fun DistanceRecord.toPlatformDistanceRecord() =
+ PlatformDistanceRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ distance.toPlatformLength()
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun ElevationGainedRecord.toPlatformElevationGainedRecord() =
+ PlatformElevationGainedRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ elevation.toPlatformLength()
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun ExerciseSessionRecord.toPlatformExerciseSessionRecord() =
+ PlatformExerciseSessionRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ exerciseType.toPlatformExerciseSessionType()
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ notes?.let { setNotes(it) }
+ title?.let { setTitle(it) }
+ route?.let { setRoute(it.toPlatformExerciseRoute()) }
+ setLaps(laps.map { it.toPlatformExerciseLap() })
+ setSegments(segments.map { it.toPlatformExerciseSegment() })
+ }
+ .build()
+
+private fun ExerciseLap.toPlatformExerciseLap() =
+ PlatformExerciseLapBuilder(startTime, endTime)
+ .apply { length?.let { setLength(it.toPlatformLength()) } }
+ .build()
+
+private fun ExerciseRoute.toPlatformExerciseRoute() =
+ PlatformExerciseRoute(
+ route.map { location ->
+ PlatformExerciseRouteLocationBuilder(
+ location.time,
+ location.latitude,
+ location.longitude
+ )
+ .apply {
+ location.horizontalAccuracy?.let {
+ setHorizontalAccuracy(it.toPlatformLength())
+ }
+ location.verticalAccuracy?.let { setVerticalAccuracy(it.toPlatformLength()) }
+ location.altitude?.let { setAltitude(it.toPlatformLength()) }
+ }
+ .build()
+ }
+ )
+
+private fun ExerciseSegment.toPlatformExerciseSegment() =
+ PlatformExerciseSegmentBuilder(startTime, endTime, segmentType.toPlatformExerciseSegmentType())
+ .setRepetitionsCount(repetitions)
+ .build()
+
+private fun FloorsClimbedRecord.toPlatformFloorsClimbedRecord() =
+ PlatformFloorsClimbedRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime, floors)
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun HeartRateRecord.toPlatformHeartRateRecord() =
+ PlatformHeartRateRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ samples.map { it.toPlatformHeartRateSample() }
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun HeartRateRecord.Sample.toPlatformHeartRateSample() =
+ PlatformHeartRateSample(beatsPerMinute, time)
+
+private fun HeartRateVariabilityRmssdRecord.toPlatformHeartRateVariabilityRmssdRecord() =
+ PlatformHeartRateVariabilityRmssdRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ heartRateVariabilityMillis
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun HeightRecord.toPlatformHeightRecord() =
+ PlatformHeightRecordBuilder(metadata.toPlatformMetadata(), time, height.toPlatformLength())
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun HydrationRecord.toPlatformHydrationRecord() =
+ PlatformHydrationRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ volume.toPlatformVolume()
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun IntermenstrualBleedingRecord.toPlatformIntermenstrualBleedingRecord() =
+ PlatformIntermenstrualBleedingRecordBuilder(metadata.toPlatformMetadata(), time)
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun LeanBodyMassRecord.toPlatformLeanBodyMassRecord() =
+ PlatformLeanBodyMassRecordBuilder(metadata.toPlatformMetadata(), time, mass.toPlatformMass())
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun MenstruationFlowRecord.toPlatformMenstruationFlowRecord() =
+ PlatformMenstruationFlowRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ flow.toPlatformMenstruationFlow()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun MenstruationPeriodRecord.toPlatformMenstruationPeriodRecord() =
+ PlatformMenstruationPeriodRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime)
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun NutritionRecord.toPlatformNutritionRecord() =
+ PlatformNutritionRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime)
+ .setMealType(mealType.toPlatformMealType())
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ biotin?.let { setBiotin(it.toPlatformMass()) }
+ caffeine?.let { setCaffeine(it.toPlatformMass()) }
+ calcium?.let { setCalcium(it.toPlatformMass()) }
+ chloride?.let { setChloride(it.toPlatformMass()) }
+ cholesterol?.let { setCholesterol(it.toPlatformMass()) }
+ chromium?.let { setChromium(it.toPlatformMass()) }
+ copper?.let { setCopper(it.toPlatformMass()) }
+ dietaryFiber?.let { setDietaryFiber(it.toPlatformMass()) }
+ energy?.let { setEnergy(it.toPlatformEnergy()) }
+ energyFromFat?.let { setEnergyFromFat(it.toPlatformEnergy()) }
+ folate?.let { setFolate(it.toPlatformMass()) }
+ folicAcid?.let { setFolicAcid(it.toPlatformMass()) }
+ iodine?.let { setIodine(it.toPlatformMass()) }
+ iron?.let { setIron(it.toPlatformMass()) }
+ magnesium?.let { setMagnesium(it.toPlatformMass()) }
+ manganese?.let { setManganese(it.toPlatformMass()) }
+ molybdenum?.let { setMolybdenum(it.toPlatformMass()) }
+ monounsaturatedFat?.let { setMonounsaturatedFat(it.toPlatformMass()) }
+ name?.let { setMealName(it) }
+ niacin?.let { setNiacin(it.toPlatformMass()) }
+ pantothenicAcid?.let { setPantothenicAcid(it.toPlatformMass()) }
+ phosphorus?.let { setPhosphorus(it.toPlatformMass()) }
+ polyunsaturatedFat?.let { setPolyunsaturatedFat(it.toPlatformMass()) }
+ potassium?.let { setPotassium(it.toPlatformMass()) }
+ protein?.let { setProtein(it.toPlatformMass()) }
+ riboflavin?.let { setRiboflavin(it.toPlatformMass()) }
+ saturatedFat?.let { setSaturatedFat(it.toPlatformMass()) }
+ selenium?.let { setSelenium(it.toPlatformMass()) }
+ sodium?.let { setSodium(it.toPlatformMass()) }
+ sugar?.let { setSugar(it.toPlatformMass()) }
+ thiamin?.let { setThiamin(it.toPlatformMass()) }
+ totalCarbohydrate?.let { setTotalCarbohydrate(it.toPlatformMass()) }
+ totalFat?.let { setTotalFat(it.toPlatformMass()) }
+ transFat?.let { setTransFat(it.toPlatformMass()) }
+ unsaturatedFat?.let { setUnsaturatedFat(it.toPlatformMass()) }
+ vitaminA?.let { setVitaminA(it.toPlatformMass()) }
+ vitaminB6?.let { setVitaminB6(it.toPlatformMass()) }
+ vitaminB12?.let { setVitaminB12(it.toPlatformMass()) }
+ vitaminC?.let { setVitaminC(it.toPlatformMass()) }
+ vitaminD?.let { setVitaminD(it.toPlatformMass()) }
+ vitaminE?.let { setVitaminE(it.toPlatformMass()) }
+ vitaminK?.let { setVitaminK(it.toPlatformMass()) }
+ zinc?.let { setZinc(it.toPlatformMass()) }
+ }
+ .build()
+
+private fun OvulationTestRecord.toPlatformOvulationTestRecord() =
+ PlatformOvulationTestRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ result.toPlatformOvulationTestResult()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun OxygenSaturationRecord.toPlatformOxygenSaturationRecord() =
+ PlatformOxygenSaturationRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ percentage.toPlatformPercentage()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun PowerRecord.toPlatformPowerRecord() =
+ PlatformPowerRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ samples.map { it.toPlatformPowerRecordSample() }
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun PowerRecord.Sample.toPlatformPowerRecordSample() =
+ PlatformPowerRecordSample(power.toPlatformPower(), time)
+
+private fun RespiratoryRateRecord.toPlatformRespiratoryRateRecord() =
+ PlatformRespiratoryRateRecordBuilder(metadata.toPlatformMetadata(), time, rate)
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun RestingHeartRateRecord.toPlatformRestingHeartRateRecord() =
+ PlatformRestingHeartRateRecordBuilder(metadata.toPlatformMetadata(), time, beatsPerMinute)
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun SexualActivityRecord.toPlatformSexualActivityRecord() =
+ PlatformSexualActivityRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ protectionUsed.toPlatformSexualActivityProtectionUsed()
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun SleepSessionRecord.toPlatformSleepSessionRecord() =
+ PlatformSleepSessionRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime)
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ notes?.let { setNotes(it) }
+ title?.let { setTitle(it) }
+ setStages(stages.map { it.toPlatformSleepSessionStage() })
+ }
+ .build()
+
+private fun SleepSessionRecord.Stage.toPlatformSleepSessionStage() =
+ PlatformSleepSessionStage(startTime, endTime, stage.toPlatformSleepStageType())
+
+private fun SpeedRecord.toPlatformSpeedRecord() =
+ PlatformSpeedRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ samples.map { it.toPlatformSpeedRecordSample() }
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun SpeedRecord.Sample.toPlatformSpeedRecordSample() =
+ PlatformSpeedSample(speed.toPlatformVelocity(), time)
+
+private fun StepsRecord.toPlatformStepsRecord() =
+ PlatformStepsRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime, count)
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun StepsCadenceRecord.toPlatformStepsCadenceRecord() =
+ PlatformStepsCadenceRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ samples.map { it.toPlatformStepsCadenceSample() }
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun StepsCadenceRecord.Sample.toPlatformStepsCadenceSample() =
+ PlatformStepsCadenceSample(rate, time)
+
+private fun TotalCaloriesBurnedRecord.toPlatformTotalCaloriesBurnedRecord() =
+ PlatformTotalCaloriesBurnedRecordBuilder(
+ metadata.toPlatformMetadata(),
+ startTime,
+ endTime,
+ energy.toPlatformEnergy()
+ )
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun Vo2MaxRecord.toPlatformVo2MaxRecord() =
+ PlatformVo2MaxRecordBuilder(
+ metadata.toPlatformMetadata(),
+ time,
+ measurementMethod.toPlatformVo2MaxMeasurementMethod(),
+ vo2MillilitersPerMinuteKilogram
+ )
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun WeightRecord.toPlatformWeightRecord() =
+ PlatformWeightRecordBuilder(metadata.toPlatformMetadata(), time, weight.toPlatformMass())
+ .apply { zoneOffset?.let { setZoneOffset(it) } }
+ .build()
+
+private fun WheelchairPushesRecord.toPlatformWheelchairPushesRecord() =
+ PlatformWheelchairPushesRecordBuilder(metadata.toPlatformMetadata(), startTime, endTime, count)
+ .apply {
+ startZoneOffset?.let { setStartZoneOffset(it) }
+ endZoneOffset?.let { setEndZoneOffset(it) }
+ }
+ .build()
+
+private fun PlatformCyclingPedalingCadenceSample.toSdkCyclingPedalingCadenceSample() =
+ CyclingPedalingCadenceRecord.Sample(time, revolutionsPerMinute)
+
+private fun PlatformHeartRateSample.toSdkHeartRateSample() =
+ HeartRateRecord.Sample(time, beatsPerMinute)
+
+private fun PlatformPowerRecordSample.toSdkPowerRecordSample() =
+ PowerRecord.Sample(time, power.toSdkPower())
+
+private fun PlatformSpeedSample.toSdkSpeedSample() = SpeedRecord.Sample(time, speed.toSdkVelocity())
+
+private fun PlatformStepsCadenceSample.toSdkStepsCadenceSample() =
+ StepsCadenceRecord.Sample(time, rate)
+
+private fun PlatformSleepSessionStage.toSdkSleepSessionStage() =
+ SleepSessionRecord.Stage(startTime, endTime, type.toSdkSleepStageType())
+
+internal fun PlatformExerciseRoute.toSdkExerciseRoute() =
+ ExerciseRoute(
+ routeLocations.map { value ->
+ ExerciseRoute.Location(
+ time = value.time,
+ latitude = value.latitude,
+ longitude = value.longitude,
+ horizontalAccuracy = value.horizontalAccuracy?.toSdkLength(),
+ verticalAccuracy = value.verticalAccuracy?.toSdkLength(),
+ altitude = value.altitude?.toSdkLength()
+ )
+ }
+ )
+
+internal fun PlatformExerciseLap.toSdkExerciseLap() =
+ ExerciseLap(startTime, endTime, length?.toSdkLength())
+
+internal fun PlatformExerciseSegment.toSdkExerciseSegment() =
+ ExerciseSegment(startTime, endTime, segmentType.toSdkExerciseSegmentType(), repetitionsCount)
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordMappings.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordMappings.kt
new file mode 100644
index 0000000..46d32a4
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordMappings.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
+import androidx.health.connect.client.records.BasalBodyTemperatureRecord
+import androidx.health.connect.client.records.BasalMetabolicRateRecord
+import androidx.health.connect.client.records.BloodGlucoseRecord
+import androidx.health.connect.client.records.BloodPressureRecord
+import androidx.health.connect.client.records.BodyFatRecord
+import androidx.health.connect.client.records.BodyTemperatureRecord
+import androidx.health.connect.client.records.BodyWaterMassRecord
+import androidx.health.connect.client.records.BoneMassRecord
+import androidx.health.connect.client.records.CervicalMucusRecord
+import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
+import androidx.health.connect.client.records.DistanceRecord
+import androidx.health.connect.client.records.ElevationGainedRecord
+import androidx.health.connect.client.records.ExerciseSessionRecord
+import androidx.health.connect.client.records.FloorsClimbedRecord
+import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
+import androidx.health.connect.client.records.HeightRecord
+import androidx.health.connect.client.records.HydrationRecord
+import androidx.health.connect.client.records.IntermenstrualBleedingRecord
+import androidx.health.connect.client.records.LeanBodyMassRecord
+import androidx.health.connect.client.records.MenstruationFlowRecord
+import androidx.health.connect.client.records.MenstruationPeriodRecord
+import androidx.health.connect.client.records.NutritionRecord
+import androidx.health.connect.client.records.OvulationTestRecord
+import androidx.health.connect.client.records.OxygenSaturationRecord
+import androidx.health.connect.client.records.PowerRecord
+import androidx.health.connect.client.records.Record
+import androidx.health.connect.client.records.RespiratoryRateRecord
+import androidx.health.connect.client.records.RestingHeartRateRecord
+import androidx.health.connect.client.records.SexualActivityRecord
+import androidx.health.connect.client.records.SleepSessionRecord
+import androidx.health.connect.client.records.SpeedRecord
+import androidx.health.connect.client.records.StepsCadenceRecord
+import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
+import androidx.health.connect.client.records.Vo2MaxRecord
+import androidx.health.connect.client.records.WeightRecord
+import androidx.health.connect.client.records.WheelchairPushesRecord
+import kotlin.reflect.KClass
+
+internal val SDK_TO_PLATFORM_RECORD_CLASS: Map<KClass<out Record>, Class<out PlatformRecord>> =
+ mapOf(
+ ActiveCaloriesBurnedRecord::class to PlatformActiveCaloriesBurnedRecord::class.java,
+ BasalBodyTemperatureRecord::class to PlatformBasalBodyTemperatureRecord::class.java,
+ BasalMetabolicRateRecord::class to PlatformBasalMetabolicRateRecord::class.java,
+ BloodGlucoseRecord::class to PlatformBloodGlucoseRecord::class.java,
+ BloodPressureRecord::class to PlatformBloodPressureRecord::class.java,
+ BodyFatRecord::class to PlatformBodyFatRecord::class.java,
+ BodyTemperatureRecord::class to PlatformBodyTemperatureRecord::class.java,
+ BodyWaterMassRecord::class to PlatformBodyWaterMassRecord::class.java,
+ BoneMassRecord::class to PlatformBoneMassRecord::class.java,
+ CervicalMucusRecord::class to PlatformCervicalMucusRecord::class.java,
+ CyclingPedalingCadenceRecord::class to PlatformCyclingPedalingCadenceRecord::class.java,
+ DistanceRecord::class to PlatformDistanceRecord::class.java,
+ ElevationGainedRecord::class to PlatformElevationGainedRecord::class.java,
+ ExerciseSessionRecord::class to PlatformExerciseSessionRecord::class.java,
+ FloorsClimbedRecord::class to PlatformFloorsClimbedRecord::class.java,
+ HeartRateRecord::class to PlatformHeartRateRecord::class.java,
+ HeartRateVariabilityRmssdRecord::class to
+ PlatformHeartRateVariabilityRmssdRecord::class.java,
+ HeightRecord::class to PlatformHeightRecord::class.java,
+ HydrationRecord::class to PlatformHydrationRecord::class.java,
+ IntermenstrualBleedingRecord::class to PlatformIntermenstrualBleedingRecord::class.java,
+ LeanBodyMassRecord::class to PlatformLeanBodyMassRecord::class.java,
+ MenstruationFlowRecord::class to PlatformMenstruationFlowRecord::class.java,
+ MenstruationPeriodRecord::class to PlatformMenstruationPeriodRecord::class.java,
+ NutritionRecord::class to PlatformNutritionRecord::class.java,
+ OvulationTestRecord::class to PlatformOvulationTestRecord::class.java,
+ OxygenSaturationRecord::class to PlatformOxygenSaturationRecord::class.java,
+ PowerRecord::class to PlatformPowerRecord::class.java,
+ RespiratoryRateRecord::class to PlatformRespiratoryRateRecord::class.java,
+ RestingHeartRateRecord::class to PlatformRestingHeartRateRecord::class.java,
+ SexualActivityRecord::class to PlatformSexualActivityRecord::class.java,
+ SleepSessionRecord::class to PlatformSleepSessionRecord::class.java,
+ SpeedRecord::class to PlatformSpeedRecord::class.java,
+ StepsCadenceRecord::class to PlatformStepsCadenceRecord::class.java,
+ StepsRecord::class to PlatformStepsRecord::class.java,
+ TotalCaloriesBurnedRecord::class to PlatformTotalCaloriesBurnedRecord::class.java,
+ Vo2MaxRecord::class to PlatformVo2MaxRecord::class.java,
+ WeightRecord::class to PlatformWeightRecord::class.java,
+ WheelchairPushesRecord::class to PlatformWheelchairPushesRecord::class.java,
+ )
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RequestConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RequestConverters.kt
new file mode 100644
index 0000000..cce4e13
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RequestConverters.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.health.connect.AggregateRecordsRequest
+import android.health.connect.LocalTimeRangeFilter
+import android.health.connect.ReadRecordsRequestUsingFilters
+import android.health.connect.TimeInstantRangeFilter
+import android.health.connect.TimeRangeFilter as PlatformTimeRangeFilter
+import android.health.connect.changelog.ChangeLogTokenRequest
+import android.health.connect.datatypes.AggregationType
+import android.health.connect.datatypes.Record as PlatformRecord
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.aggregate.AggregateMetric
+import androidx.health.connect.client.records.Record
+import androidx.health.connect.client.request.AggregateGroupByDurationRequest
+import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
+import androidx.health.connect.client.request.AggregateRequest
+import androidx.health.connect.client.request.ChangesTokenRequest
+import androidx.health.connect.client.request.ReadRecordsRequest
+import androidx.health.connect.client.time.TimeRangeFilter
+import java.time.Instant
+
+fun ReadRecordsRequest<out Record>.toPlatformRequest():
+ ReadRecordsRequestUsingFilters<out PlatformRecord> {
+ return ReadRecordsRequestUsingFilters.Builder(recordType.toPlatformRecordClass())
+ .setTimeRangeFilter(timeRangeFilter.toPlatformTimeRangeFilter())
+ .setPageSize(pageSize)
+ .apply {
+ dataOriginFilter.forEach { addDataOrigins(it.toPlatformDataOrigin()) }
+ pageToken?.let { setPageToken(it.toLong()) }
+ // Platform doesn't allow setting both pageToken and ascendingOrder together.
+ if (pageToken == null) {
+ setAscending(ascendingOrder)
+ }
+ }
+ .build()
+}
+
+fun TimeRangeFilter.toPlatformTimeRangeFilter(): PlatformTimeRangeFilter {
+ return if (startTime != null || endTime != null) {
+ TimeInstantRangeFilter.Builder().setStartTime(startTime).setEndTime(endTime).build()
+ } else if (localStartTime != null || localEndTime != null) {
+ LocalTimeRangeFilter.Builder().setStartTime(localStartTime).setEndTime(localEndTime).build()
+ } else {
+ // Platform doesn't allow both startTime and endTime to be null
+ TimeInstantRangeFilter.Builder().setStartTime(Instant.EPOCH).build()
+ }
+}
+
+fun ChangesTokenRequest.toPlatformRequest(): ChangeLogTokenRequest {
+ return ChangeLogTokenRequest.Builder()
+ .apply {
+ dataOriginFilters.forEach { addDataOriginFilter(it.toPlatformDataOrigin()) }
+ recordTypes.forEach { addRecordType(it.toPlatformRecordClass()) }
+ }
+ .build()
+}
+
+fun AggregateRequest.toPlatformRequest(): AggregateRecordsRequest<Any> {
+ return AggregateRecordsRequest.Builder<Any>(timeRangeFilter.toPlatformTimeRangeFilter())
+ .apply {
+ dataOriginFilter.forEach { addDataOriginsFilter(it.toPlatformDataOrigin()) }
+ metrics.forEach { addAggregationType(it.toAggregationType()) }
+ }
+ .build()
+}
+
+fun AggregateGroupByDurationRequest.toPlatformRequest(): AggregateRecordsRequest<Any> {
+ return AggregateRecordsRequest.Builder<Any>(timeRangeFilter.toPlatformTimeRangeFilter())
+ .apply {
+ dataOriginFilter.forEach { addDataOriginsFilter(it.toPlatformDataOrigin()) }
+ metrics.forEach { addAggregationType(it.toAggregationType()) }
+ }
+ .build()
+}
+
+fun AggregateGroupByPeriodRequest.toPlatformRequest(): AggregateRecordsRequest<Any> {
+ return AggregateRecordsRequest.Builder<Any>(timeRangeFilter.toPlatformTimeRangeFilter())
+ .apply {
+ dataOriginFilter.forEach { addDataOriginsFilter(it.toPlatformDataOrigin()) }
+ metrics.forEach { addAggregationType(it.toAggregationType()) }
+ }
+ .build()
+}
+
+@Suppress("UNCHECKED_CAST")
+fun AggregateMetric<Any>.toAggregationType(): AggregationType<Any> {
+ return DOUBLE_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: DURATION_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: ENERGY_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: LENGTH_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: LONG_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: GRAMS_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: KILOGRAMS_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: POWER_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: VOLUME_AGGREGATION_METRIC_TYPE_MAP[this] as AggregationType<Any>?
+ ?: throw IllegalArgumentException("Unsupported aggregation type $metricKey")
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/ResponseConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/ResponseConverters.kt
new file mode 100644
index 0000000..d1fd401a9
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/ResponseConverters.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.health.connect.AggregateRecordsGroupedByDurationResponse
+import android.health.connect.AggregateRecordsGroupedByPeriodResponse
+import android.health.connect.AggregateRecordsResponse
+import android.health.connect.datatypes.AggregationType
+import android.health.connect.datatypes.units.Energy as PlatformEnergy
+import android.health.connect.datatypes.units.Volume as PlatformVolume
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.health.connect.client.aggregate.AggregateMetric
+import androidx.health.connect.client.aggregate.AggregationResult
+import androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration
+import androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod
+import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.Mass
+import java.time.ZoneOffset
+
+fun AggregateRecordsResponse<Any>.toSdkResponse(metrics: Set<AggregateMetric<Any>>) =
+ buildAggregationResult(metrics, ::get)
+
+fun AggregateRecordsGroupedByDurationResponse<Any>.toSdkResponse(
+ metrics: Set<AggregateMetric<Any>>
+) =
+ AggregationResultGroupedByDuration(
+ buildAggregationResult(metrics, ::get),
+ startTime,
+ endTime,
+ getZoneOffset(metrics.first().toAggregationType())
+ ?: ZoneOffset.systemDefault().rules.getOffset(startTime)
+ )
+
+fun AggregateRecordsGroupedByPeriodResponse<Any>.toSdkResponse(metrics: Set<AggregateMetric<Any>>) =
+ AggregationResultGroupedByPeriod(buildAggregationResult(metrics, ::get), startTime, endTime)
+
+private fun buildAggregationResult(
+ metrics: Set<AggregateMetric<Any>>,
+ aggregationValueGetter: (AggregationType<Any>) -> Any?
+): AggregationResult {
+ val metricValueMap = buildMap {
+ metrics.forEach { metric ->
+ aggregationValueGetter(metric.toAggregationType())?.also { this[metric] = it }
+ }
+ }
+ return AggregationResult(
+ getLongMetricValues(metricValueMap),
+ getDoubleMetricValues(metricValueMap),
+ setOf()
+ )
+}
+
+@VisibleForTesting
+internal fun getLongMetricValues(
+ metricValueMap: Map<AggregateMetric<Any>, Any>
+): Map<String, Long> {
+ return buildMap {
+ metricValueMap.forEach { (key, value) ->
+ if (
+ key in DURATION_AGGREGATION_METRIC_TYPE_MAP ||
+ key in LONG_AGGREGATION_METRIC_TYPE_MAP
+ ) {
+ this[key.metricKey] = value as Long
+ }
+ }
+ }
+}
+
+@VisibleForTesting
+internal fun getDoubleMetricValues(
+ metricValueMap: Map<AggregateMetric<Any>, Any>
+): Map<String, Double> {
+ return buildMap {
+ metricValueMap.forEach { (key, value) ->
+ when (key) {
+ in DOUBLE_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = value as Double
+ }
+ in ENERGY_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] =
+ Energy.calories((value as PlatformEnergy).inCalories).inKilocalories
+ }
+ in LENGTH_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = (value as PlatformLength).inMeters
+ }
+ in GRAMS_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = (value as PlatformMass).inGrams
+ }
+ in KILOGRAMS_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = Mass.grams((value as PlatformMass).inGrams).inKilograms
+ }
+ in POWER_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = (value as PlatformPower).inWatts
+ }
+ in VOLUME_AGGREGATION_METRIC_TYPE_MAP -> {
+ this[key.metricKey] = (value as PlatformVolume).inLiters
+ }
+ }
+ }
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/UnitConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/UnitConverters.kt
new file mode 100644
index 0000000..c9a23f9
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/UnitConverters.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+@file:RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+
+package androidx.health.connect.client.impl.platform.records
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.units.BloodGlucose
+import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.Length
+import androidx.health.connect.client.units.Mass
+import androidx.health.connect.client.units.Percentage
+import androidx.health.connect.client.units.Power
+import androidx.health.connect.client.units.Pressure
+import androidx.health.connect.client.units.Temperature
+import androidx.health.connect.client.units.Velocity
+import androidx.health.connect.client.units.Volume
+
+internal fun BloodGlucose.toPlatformBloodGlucose(): PlatformBloodGlucose {
+ return PlatformBloodGlucose.fromMillimolesPerLiter(inMillimolesPerLiter)
+}
+
+internal fun Energy.toPlatformEnergy(): PlatformEnergy {
+ return PlatformEnergy.fromCalories(inCalories)
+}
+
+internal fun Length.toPlatformLength(): PlatformLength {
+ return PlatformLength.fromMeters(inMeters)
+}
+
+internal fun Mass.toPlatformMass(): PlatformMass {
+ return PlatformMass.fromGrams(inGrams)
+}
+
+internal fun Percentage.toPlatformPercentage(): PlatformPercentage {
+ return PlatformPercentage.fromValue(value)
+}
+
+internal fun Power.toPlatformPower(): PlatformPower {
+ return PlatformPower.fromWatts(inWatts)
+}
+
+internal fun Pressure.toPlatformPressure(): PlatformPressure {
+ return PlatformPressure.fromMillimetersOfMercury(inMillimetersOfMercury)
+}
+
+internal fun Temperature.toPlatformTemperature(): PlatformTemperature {
+ return PlatformTemperature.fromCelsius(inCelsius)
+}
+
+internal fun Velocity.toPlatformVelocity(): PlatformVelocity {
+ return PlatformVelocity.fromMetersPerSecond(inMetersPerSecond)
+}
+
+internal fun Volume.toPlatformVolume(): PlatformVolume {
+ return PlatformVolume.fromLiters(inLiters)
+}
+
+internal fun PlatformBloodGlucose.toSdkBloodGlucose(): BloodGlucose {
+ return BloodGlucose.millimolesPerLiter(inMillimolesPerLiter)
+}
+
+internal fun PlatformEnergy.toSdkEnergy(): Energy {
+ return Energy.calories(inCalories)
+}
+
+internal fun PlatformLength.toSdkLength(): Length {
+ return Length.meters(inMeters)
+}
+
+internal fun PlatformMass.toSdkMass(): Mass {
+ return Mass.grams(inGrams)
+}
+
+internal fun PlatformPercentage.toSdkPercentage(): Percentage {
+ return Percentage(value)
+}
+
+internal fun PlatformPower.toSdkPower(): Power {
+ return Power.watts(inWatts)
+}
+
+internal fun PlatformPressure.toSdkPressure(): Pressure {
+ return Pressure.millimetersOfMercury(inMillimetersOfMercury)
+}
+
+internal fun PlatformTemperature.toSdkTemperature(): Temperature {
+ return Temperature.celsius(inCelsius)
+}
+
+internal fun PlatformVelocity.toSdkVelocity(): Velocity {
+ return Velocity.metersPerSecond(inMetersPerSecond)
+}
+
+internal fun PlatformVolume.toSdkVolume(): Volume {
+ return Volume.liters(inLiters)
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java
new file mode 100644
index 0000000..c5b8a8e
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Helps with conversions to the platform record and API objects.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.health.connect.client.impl.platform.records;
+
+import androidx.annotation.RestrictTo;
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/InsertRecordsResponseConverter.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/InsertRecordsResponseConverter.kt
new file mode 100644
index 0000000..10903a2
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/InsertRecordsResponseConverter.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(api = 34)
+
+package androidx.health.connect.client.impl.platform.response
+
+import android.health.connect.InsertRecordsResponse
+import androidx.annotation.RequiresApi
+
+internal fun InsertRecordsResponse.toKtResponse():
+ androidx.health.connect.client.response.InsertRecordsResponse {
+ return androidx.health.connect.client.response.InsertRecordsResponse(
+ recordIdsList = records.map { record -> record.metadata.id }
+ )
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java
new file mode 100644
index 0000000..f8b9cb7
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/response/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Helps with conversions to the platform record and API objects.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.health.connect.client.impl.platform.response;
+
+import androidx.annotation.RestrictTo;
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
index 5103509..d09bd4e 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
@@ -129,138 +129,128 @@
return WRITE_PERMISSION_PREFIX + RECORD_TYPE_TO_PERMISSION.getOrDefault(recordType, "")
}
+ internal const val PERMISSION_PREFIX = "android.permission.health."
+
// Read permissions for ACTIVITY.
internal const val READ_ACTIVE_CALORIES_BURNED =
- "android.permission.health.READ_ACTIVE_CALORIES_BURNED"
- internal const val READ_DISTANCE = "android.permission.health.READ_DISTANCE"
- internal const val READ_ELEVATION_GAINED = "android.permission.health.READ_ELEVATION_GAINED"
- internal const val READ_EXERCISE = "android.permission.health.READ_EXERCISE"
- internal const val READ_FLOORS_CLIMBED = "android.permission.health.READ_FLOORS_CLIMBED"
- internal const val READ_STEPS = "android.permission.health.READ_STEPS"
+ PERMISSION_PREFIX + "READ_ACTIVE_CALORIES_BURNED"
+ internal const val READ_DISTANCE = PERMISSION_PREFIX + "READ_DISTANCE"
+ internal const val READ_ELEVATION_GAINED = PERMISSION_PREFIX + "READ_ELEVATION_GAINED"
+ internal const val READ_EXERCISE = PERMISSION_PREFIX + "READ_EXERCISE"
+ internal const val READ_FLOORS_CLIMBED = PERMISSION_PREFIX + "READ_FLOORS_CLIMBED"
+ internal const val READ_STEPS = PERMISSION_PREFIX + "READ_STEPS"
internal const val READ_TOTAL_CALORIES_BURNED =
- "android.permission.health.READ_TOTAL_CALORIES_BURNED"
- internal const val READ_VO2_MAX = "android.permission.health.READ_VO2_MAX"
- internal const val READ_WHEELCHAIR_PUSHES =
- "android.permission.health.READ_WHEELCHAIR_PUSHES"
- internal const val READ_POWER = "android.permission.health.READ_POWER"
- internal const val READ_SPEED = "android.permission.health.READ_SPEED"
+ PERMISSION_PREFIX + "READ_TOTAL_CALORIES_BURNED"
+ internal const val READ_VO2_MAX = PERMISSION_PREFIX + "READ_VO2_MAX"
+ internal const val READ_WHEELCHAIR_PUSHES = PERMISSION_PREFIX + "READ_WHEELCHAIR_PUSHES"
+ internal const val READ_POWER = PERMISSION_PREFIX + "READ_POWER"
+ internal const val READ_SPEED = PERMISSION_PREFIX + "READ_SPEED"
// Read permissions for BODY_MEASUREMENTS.
internal const val READ_BASAL_METABOLIC_RATE =
- "android.permission.health.READ_BASAL_METABOLIC_RATE"
- internal const val READ_BODY_FAT = "android.permission.health.READ_BODY_FAT"
- internal const val READ_BODY_WATER_MASS = "android.permission.health.READ_BODY_WATER_MASS"
- internal const val READ_BONE_MASS = "android.permission.health.READ_BONE_MASS"
- internal const val READ_HEIGHT = "android.permission.health.READ_HEIGHT"
+ PERMISSION_PREFIX + "READ_BASAL_METABOLIC_RATE"
+ internal const val READ_BODY_FAT = PERMISSION_PREFIX + "READ_BODY_FAT"
+ internal const val READ_BODY_WATER_MASS = PERMISSION_PREFIX + "READ_BODY_WATER_MASS"
+ internal const val READ_BONE_MASS = PERMISSION_PREFIX + "READ_BONE_MASS"
+ internal const val READ_HEIGHT = PERMISSION_PREFIX + "READ_HEIGHT"
@RestrictTo(RestrictTo.Scope.LIBRARY)
- internal const val READ_HIP_CIRCUMFERENCE =
- "android.permission.health.READ_HIP_CIRCUMFERENCE"
- internal const val READ_LEAN_BODY_MASS = "android.permission.health.READ_LEAN_BODY_MASS"
+ internal const val READ_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_HIP_CIRCUMFERENCE"
+ internal const val READ_LEAN_BODY_MASS = PERMISSION_PREFIX + "READ_LEAN_BODY_MASS"
@RestrictTo(RestrictTo.Scope.LIBRARY)
- internal const val READ_WAIST_CIRCUMFERENCE =
- "android.permission.health.READ_WAIST_CIRCUMFERENCE"
- internal const val READ_WEIGHT = "android.permission.health.READ_WEIGHT"
+ internal const val READ_WAIST_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_WAIST_CIRCUMFERENCE"
+ internal const val READ_WEIGHT = PERMISSION_PREFIX + "READ_WEIGHT"
// Read permissions for CYCLE_TRACKING.
- internal const val READ_CERVICAL_MUCUS = "android.permission.health.READ_CERVICAL_MUCUS"
+ internal const val READ_CERVICAL_MUCUS = PERMISSION_PREFIX + "READ_CERVICAL_MUCUS"
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal const val READ_INTERMENSTRUAL_BLEEDING =
- "android.permission.health.READ_INTERMENSTRUAL_BLEEDING"
- internal const val READ_MENSTRUATION = "android.permission.health.READ_MENSTRUATION"
- internal const val READ_OVULATION_TEST = "android.permission.health.READ_OVULATION_TEST"
- internal const val READ_SEXUAL_ACTIVITY = "android.permission.health.READ_SEXUAL_ACTIVITY"
+ PERMISSION_PREFIX + "READ_INTERMENSTRUAL_BLEEDING"
+ internal const val READ_MENSTRUATION = PERMISSION_PREFIX + "READ_MENSTRUATION"
+ internal const val READ_OVULATION_TEST = PERMISSION_PREFIX + "READ_OVULATION_TEST"
+ internal const val READ_SEXUAL_ACTIVITY = PERMISSION_PREFIX + "READ_SEXUAL_ACTIVITY"
// Read permissions for NUTRITION.
- internal const val READ_HYDRATION = "android.permission.health.READ_HYDRATION"
- internal const val READ_NUTRITION = "android.permission.health.READ_NUTRITION"
+ internal const val READ_HYDRATION = PERMISSION_PREFIX + "READ_HYDRATION"
+ internal const val READ_NUTRITION = PERMISSION_PREFIX + "READ_NUTRITION"
// Read permissions for SLEEP.
- internal const val READ_SLEEP = "android.permission.health.READ_SLEEP"
+ internal const val READ_SLEEP = PERMISSION_PREFIX + "READ_SLEEP"
// Read permissions for VITALS.
internal const val READ_BASAL_BODY_TEMPERATURE =
- "android.permission.health.READ_BASAL_BODY_TEMPERATURE"
- internal const val READ_BLOOD_GLUCOSE = "android.permission.health.READ_BLOOD_GLUCOSE"
- internal const val READ_BLOOD_PRESSURE = "android.permission.health.READ_BLOOD_PRESSURE"
- internal const val READ_BODY_TEMPERATURE = "android.permission.health.READ_BODY_TEMPERATURE"
- internal const val READ_HEART_RATE = "android.permission.health.READ_HEART_RATE"
+ PERMISSION_PREFIX + "READ_BASAL_BODY_TEMPERATURE"
+ internal const val READ_BLOOD_GLUCOSE = PERMISSION_PREFIX + "READ_BLOOD_GLUCOSE"
+ internal const val READ_BLOOD_PRESSURE = PERMISSION_PREFIX + "READ_BLOOD_PRESSURE"
+ internal const val READ_BODY_TEMPERATURE = PERMISSION_PREFIX + "READ_BODY_TEMPERATURE"
+ internal const val READ_HEART_RATE = PERMISSION_PREFIX + "READ_HEART_RATE"
internal const val READ_HEART_RATE_VARIABILITY =
- "android.permission.health.READ_HEART_RATE_VARIABILITY"
- internal const val READ_OXYGEN_SATURATION =
- "android.permission.health.READ_OXYGEN_SATURATION"
- internal const val READ_RESPIRATORY_RATE = "android.permission.health.READ_RESPIRATORY_RATE"
- internal const val READ_RESTING_HEART_RATE =
- "android.permission.health.READ_RESTING_HEART_RATE"
+ PERMISSION_PREFIX + "READ_HEART_RATE_VARIABILITY"
+ internal const val READ_OXYGEN_SATURATION = PERMISSION_PREFIX + "READ_OXYGEN_SATURATION"
+ internal const val READ_RESPIRATORY_RATE = PERMISSION_PREFIX + "READ_RESPIRATORY_RATE"
+ internal const val READ_RESTING_HEART_RATE = PERMISSION_PREFIX + "READ_RESTING_HEART_RATE"
// Write permissions for ACTIVITY.
internal const val WRITE_ACTIVE_CALORIES_BURNED =
- "android.permission.health.WRITE_ACTIVE_CALORIES_BURNED"
- internal const val WRITE_DISTANCE = "android.permission.health.WRITE_DISTANCE"
- internal const val WRITE_ELEVATION_GAINED =
- "android.permission.health.WRITE_ELEVATION_GAINED"
- internal const val WRITE_EXERCISE = "android.permission.health.WRITE_EXERCISE"
- internal const val WRITE_EXERCISE_ROUTE = "android.permission.health.WRITE_EXERCISE_ROUTE"
- internal const val WRITE_FLOORS_CLIMBED = "android.permission.health.WRITE_FLOORS_CLIMBED"
- internal const val WRITE_STEPS = "android.permission.health.WRITE_STEPS"
+ PERMISSION_PREFIX + "WRITE_ACTIVE_CALORIES_BURNED"
+ internal const val WRITE_DISTANCE = PERMISSION_PREFIX + "WRITE_DISTANCE"
+ internal const val WRITE_ELEVATION_GAINED = PERMISSION_PREFIX + "WRITE_ELEVATION_GAINED"
+ internal const val WRITE_EXERCISE = PERMISSION_PREFIX + "WRITE_EXERCISE"
+ internal const val WRITE_EXERCISE_ROUTE = PERMISSION_PREFIX + "WRITE_EXERCISE_ROUTE"
+ internal const val WRITE_FLOORS_CLIMBED = PERMISSION_PREFIX + "WRITE_FLOORS_CLIMBED"
+ internal const val WRITE_STEPS = PERMISSION_PREFIX + "WRITE_STEPS"
internal const val WRITE_TOTAL_CALORIES_BURNED =
- "android.permission.health.WRITE_TOTAL_CALORIES_BURNED"
- internal const val WRITE_VO2_MAX = "android.permission.health.WRITE_VO2_MAX"
- internal const val WRITE_WHEELCHAIR_PUSHES =
- "android.permission.health.WRITE_WHEELCHAIR_PUSHES"
- internal const val WRITE_POWER = "android.permission.health.WRITE_POWER"
- internal const val WRITE_SPEED = "android.permission.health.WRITE_SPEED"
+ PERMISSION_PREFIX + "WRITE_TOTAL_CALORIES_BURNED"
+ internal const val WRITE_VO2_MAX = PERMISSION_PREFIX + "WRITE_VO2_MAX"
+ internal const val WRITE_WHEELCHAIR_PUSHES = PERMISSION_PREFIX + "WRITE_WHEELCHAIR_PUSHES"
+ internal const val WRITE_POWER = PERMISSION_PREFIX + "WRITE_POWER"
+ internal const val WRITE_SPEED = PERMISSION_PREFIX + "WRITE_SPEED"
// Write permissions for BODY_MEASUREMENTS.
internal const val WRITE_BASAL_METABOLIC_RATE =
- "android.permission.health.WRITE_BASAL_METABOLIC_RATE"
- internal const val WRITE_BODY_FAT = "android.permission.health.WRITE_BODY_FAT"
- internal const val WRITE_BODY_WATER_MASS = "android.permission.health.WRITE_BODY_WATER_MASS"
- internal const val WRITE_BONE_MASS = "android.permission.health.WRITE_BONE_MASS"
- internal const val WRITE_HEIGHT = "android.permission.health.WRITE_HEIGHT"
+ PERMISSION_PREFIX + "WRITE_BASAL_METABOLIC_RATE"
+ internal const val WRITE_BODY_FAT = PERMISSION_PREFIX + "WRITE_BODY_FAT"
+ internal const val WRITE_BODY_WATER_MASS = PERMISSION_PREFIX + "WRITE_BODY_WATER_MASS"
+ internal const val WRITE_BONE_MASS = PERMISSION_PREFIX + "WRITE_BONE_MASS"
+ internal const val WRITE_HEIGHT = PERMISSION_PREFIX + "WRITE_HEIGHT"
@RestrictTo(RestrictTo.Scope.LIBRARY)
- internal const val WRITE_HIP_CIRCUMFERENCE =
- "android.permission.health.WRITE_HIP_CIRCUMFERENCE"
- internal const val WRITE_LEAN_BODY_MASS = "android.permission.health.WRITE_LEAN_BODY_MASS"
+ internal const val WRITE_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "WRITE_HIP_CIRCUMFERENCE"
+ internal const val WRITE_LEAN_BODY_MASS = PERMISSION_PREFIX + "WRITE_LEAN_BODY_MASS"
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal const val WRITE_WAIST_CIRCUMFERENCE =
- "android.permission.health.WRITE_WAIST_CIRCUMFERENCE"
- internal const val WRITE_WEIGHT = "android.permission.health.WRITE_WEIGHT"
+ PERMISSION_PREFIX + "WRITE_WAIST_CIRCUMFERENCE"
+ internal const val WRITE_WEIGHT = PERMISSION_PREFIX + "WRITE_WEIGHT"
// Write permissions for CYCLE_TRACKING.
- internal const val WRITE_CERVICAL_MUCUS = "android.permission.health.WRITE_CERVICAL_MUCUS"
+ internal const val WRITE_CERVICAL_MUCUS = PERMISSION_PREFIX + "WRITE_CERVICAL_MUCUS"
@RestrictTo(RestrictTo.Scope.LIBRARY)
internal const val WRITE_INTERMENSTRUAL_BLEEDING =
- "android.permission.health.WRITE_INTERMENSTRUAL_BLEEDING"
- internal const val WRITE_MENSTRUATION = "android.permission.health.WRITE_MENSTRUATION"
- internal const val WRITE_OVULATION_TEST = "android.permission.health.WRITE_OVULATION_TEST"
- internal const val WRITE_SEXUAL_ACTIVITY = "android.permission.health.WRITE_SEXUAL_ACTIVITY"
+ PERMISSION_PREFIX + "WRITE_INTERMENSTRUAL_BLEEDING"
+ internal const val WRITE_MENSTRUATION = PERMISSION_PREFIX + "WRITE_MENSTRUATION"
+ internal const val WRITE_OVULATION_TEST = PERMISSION_PREFIX + "WRITE_OVULATION_TEST"
+ internal const val WRITE_SEXUAL_ACTIVITY = PERMISSION_PREFIX + "WRITE_SEXUAL_ACTIVITY"
// Write permissions for NUTRITION.
- internal const val WRITE_HYDRATION = "android.permission.health.WRITE_HYDRATION"
- internal const val WRITE_NUTRITION = "android.permission.health.WRITE_NUTRITION"
+ internal const val WRITE_HYDRATION = PERMISSION_PREFIX + "WRITE_HYDRATION"
+ internal const val WRITE_NUTRITION = PERMISSION_PREFIX + "WRITE_NUTRITION"
// Write permissions for SLEEP.
- internal const val WRITE_SLEEP = "android.permission.health.WRITE_SLEEP"
+ internal const val WRITE_SLEEP = PERMISSION_PREFIX + "WRITE_SLEEP"
// Write permissions for VITALS.
internal const val WRITE_BASAL_BODY_TEMPERATURE =
- "android.permission.health.WRITE_BASAL_BODY_TEMPERATURE"
- internal const val WRITE_BLOOD_GLUCOSE = "android.permission.health.WRITE_BLOOD_GLUCOSE"
- internal const val WRITE_BLOOD_PRESSURE = "android.permission.health.WRITE_BLOOD_PRESSURE"
- internal const val WRITE_BODY_TEMPERATURE =
- "android.permission.health.WRITE_BODY_TEMPERATURE"
- internal const val WRITE_HEART_RATE = "android.permission.health.WRITE_HEART_RATE"
+ PERMISSION_PREFIX + "WRITE_BASAL_BODY_TEMPERATURE"
+ internal const val WRITE_BLOOD_GLUCOSE = PERMISSION_PREFIX + "WRITE_BLOOD_GLUCOSE"
+ internal const val WRITE_BLOOD_PRESSURE = PERMISSION_PREFIX + "WRITE_BLOOD_PRESSURE"
+ internal const val WRITE_BODY_TEMPERATURE = PERMISSION_PREFIX + "WRITE_BODY_TEMPERATURE"
+ internal const val WRITE_HEART_RATE = PERMISSION_PREFIX + "WRITE_HEART_RATE"
internal const val WRITE_HEART_RATE_VARIABILITY =
- "android.permission.health.WRITE_HEART_RATE_VARIABILITY"
- internal const val WRITE_OXYGEN_SATURATION =
- "android.permission.health.WRITE_OXYGEN_SATURATION"
- internal const val WRITE_RESPIRATORY_RATE =
- "android.permission.health.WRITE_RESPIRATORY_RATE"
- internal const val WRITE_RESTING_HEART_RATE =
- "android.permission.health.WRITE_RESTING_HEART_RATE"
+ PERMISSION_PREFIX + "WRITE_HEART_RATE_VARIABILITY"
+ internal const val WRITE_OXYGEN_SATURATION = PERMISSION_PREFIX + "WRITE_OXYGEN_SATURATION"
+ internal const val WRITE_RESPIRATORY_RATE = PERMISSION_PREFIX + "WRITE_RESPIRATORY_RATE"
+ internal const val WRITE_RESTING_HEART_RATE = PERMISSION_PREFIX + "WRITE_RESTING_HEART_RATE"
- internal const val READ_PERMISSION_PREFIX = "android.permission.health.READ_"
- internal const val WRITE_PERMISSION_PREFIX = "android.permission.health.WRITE_"
+ internal const val READ_PERMISSION_PREFIX = PERMISSION_PREFIX + "READ_"
+ internal const val WRITE_PERMISSION_PREFIX = PERMISSION_PREFIX + "WRITE_"
internal val RECORD_TYPE_TO_PERMISSION =
mapOf<KClass<out Record>, String>(
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCake.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCake.kt
new file mode 100644
index 0000000..e6994c5
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCake.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.health.connect.client.permission.platform
+
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_PREFIX
+
+/**
+ * An [ActivityResultContract] to request Health Connect system permissions.
+ *
+ * @see androidx.activity.ComponentActivity.registerForActivityResult
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class HealthDataRequestPermissionsUpsideDownCake :
+ ActivityResultContract<Set<String>, Set<String>>() {
+
+ private val requestPermissions = RequestMultiplePermissions()
+
+ override fun createIntent(context: Context, input: Set<String>): Intent {
+ require(input.all { it.startsWith(PERMISSION_PREFIX) }) {
+ "Unsupported health connect permission"
+ }
+ return requestPermissions.createIntent(context, input.toTypedArray())
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): Set<String> =
+ requestPermissions.parseResult(resultCode, intent).filterValues { it }.keys
+
+ override fun getSynchronousResult(
+ context: Context,
+ input: Set<String>,
+ ): SynchronousResult<Set<String>>? =
+ requestPermissions.getSynchronousResult(context, input.toTypedArray())?.let { result ->
+ SynchronousResult(result.value.filterValues { it }.keys)
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
new file mode 100644
index 0000000..8582568
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.permission.platform
+
+import android.content.Context
+import android.content.Intent
+import android.health.connect.HealthConnectManager
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.impl.platform.records.PlatformExerciseRoute
+import androidx.health.connect.client.impl.platform.records.toSdkExerciseRoute
+import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.platform.client.impl.logger.Logger
+
+/**
+ * An [ActivityResultContract] to request a route associated with an {@code ExerciseSessionRecord}.
+ *
+ * @see androidx.activity.ComponentActivity.registerForActivityResult
+ */
+@RequiresApi(34)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class RequestExerciseRouteUpsideDownCake :
+ ActivityResultContract<String?, ExerciseRoute?>() {
+ override fun createIntent(context: Context, input: String?): Intent {
+ require(!input.isNullOrEmpty()) { "Session identifier is required" }
+ return Intent(HealthConnectManager.ACTION_REQUEST_EXERCISE_ROUTE).apply {
+ putExtra(HealthConnectManager.EXTRA_SESSION_ID, input)
+ }
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
+ val route =
+ intent?.getParcelableExtra(
+ HealthConnectManager.EXTRA_EXERCISE_ROUTE,
+ PlatformExerciseRoute::class.java
+ )
+ if (route == null) {
+ Logger.debug(HealthConnectClient.HEALTH_CONNECT_CLIENT_TAG, "No route returned.")
+ return null
+ }
+ Logger.debug(HealthConnectClient.HEALTH_CONNECT_CLIENT_TAG, "Returned a route.")
+ return route.toSdkExerciseRoute()
+ }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocation.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocation.kt
index 6b77845..1b4c836 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocation.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocation.kt
@@ -67,8 +67,10 @@
/**
* Where on the user's body a temperature measurement was taken from.
+ *
* @suppress
*/
+@Target(AnnotationTarget.PROPERTY, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.SOURCE)
@IntDef(
value =
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
index d839bb1..d8b5428 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Build
+import androidx.health.connect.client.impl.HealthConnectClientImpl
import androidx.health.platform.client.HealthDataService
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -50,10 +51,7 @@
@Suppress("Deprecation")
fun noBackingImplementation_unavailable() {
val packageManager = context.packageManager
- Shadows.shadowOf(packageManager).removePackage(PROVIDER_PACKAGE_NAME)
- assertThat(HealthConnectClient.isApiSupported()).isTrue()
- assertThat(HealthConnectClient.isProviderAvailable(context, PROVIDER_PACKAGE_NAME))
- .isFalse()
+ shadowOf(packageManager).removePackage(PROVIDER_PACKAGE_NAME)
assertThat(HealthConnectClient.getSdkStatus(context, PROVIDER_PACKAGE_NAME))
.isEqualTo(HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED)
assertThrows(IllegalStateException::class.java) {
@@ -66,8 +64,6 @@
@Suppress("Deprecation")
fun backingImplementation_notEnabled_unavailable() {
installPackage(context, PROVIDER_PACKAGE_NAME, versionCode = 35001, enabled = false)
- assertThat(HealthConnectClient.isProviderAvailable(context, PROVIDER_PACKAGE_NAME))
- .isFalse()
assertThat(HealthConnectClient.getSdkStatus(context, PROVIDER_PACKAGE_NAME))
.isEqualTo(HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED)
assertThrows(IllegalStateException::class.java) {
@@ -80,8 +76,6 @@
@Suppress("Deprecation")
fun backingImplementation_enabledNoService_unavailable() {
installPackage(context, PROVIDER_PACKAGE_NAME, versionCode = 35001, enabled = true)
- assertThat(HealthConnectClient.isProviderAvailable(context, PROVIDER_PACKAGE_NAME))
- .isFalse()
assertThat(HealthConnectClient.getSdkStatus(context, PROVIDER_PACKAGE_NAME))
.isEqualTo(HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED)
assertThrows(IllegalStateException::class.java) {
@@ -97,15 +91,13 @@
context,
HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME,
versionCode = HealthConnectClient.DEFAULT_PROVIDER_MIN_VERSION_CODE - 1,
- enabled = true)
+ enabled = true
+ )
installService(context, HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME)
- assertThat(HealthConnectClient.isProviderAvailable(context)).isFalse()
assertThat(HealthConnectClient.getSdkStatus(context, PROVIDER_PACKAGE_NAME))
.isEqualTo(HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED)
- assertThrows(IllegalStateException::class.java) {
- HealthConnectClient.getOrCreate(context)
- }
+ assertThrows(IllegalStateException::class.java) { HealthConnectClient.getOrCreate(context) }
}
@Test
@@ -116,23 +108,48 @@
context,
HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME,
versionCode = HealthConnectClient.DEFAULT_PROVIDER_MIN_VERSION_CODE,
- enabled = true)
+ enabled = true
+ )
installService(context, HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME)
- assertThat(HealthConnectClient.isProviderAvailable(context)).isTrue()
- assertThat(HealthConnectClient.getSdkStatus(
- context, HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME))
+ assertThat(
+ HealthConnectClient.getSdkStatus(
+ context,
+ HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME
+ )
+ )
.isEqualTo(HealthConnectClient.SDK_AVAILABLE)
- HealthConnectClient.getOrCreate(context)
+ assertThat(HealthConnectClient.getOrCreate(context))
+ .isInstanceOf(HealthConnectClientImpl::class.java)
+ }
+
+ @Test
+ @Config(sdk = [Build.VERSION_CODES.P])
+ @Suppress("Deprecation")
+ fun backingImplementationLegacy_enabledSupportedVersion_isAvailable() {
+ installPackage(
+ context,
+ HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME,
+ versionCode = HealthConnectClient.DEFAULT_PROVIDER_MIN_VERSION_CODE,
+ enabled = true
+ )
+ installService(context, HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME)
+
+ assertThat(
+ HealthConnectClient.getSdkStatusLegacy(
+ context,
+ HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME
+ )
+ )
+ .isEqualTo(HealthConnectClient.SDK_AVAILABLE)
+ assertThat(HealthConnectClient.getOrCreateLegacy(context))
+ .isInstanceOf(HealthConnectClientImpl::class.java)
}
@Test
@Config(sdk = [Build.VERSION_CODES.O_MR1])
@Suppress("Deprecation")
fun sdkVersionTooOld_unavailable() {
- assertThat(HealthConnectClient.isApiSupported()).isFalse()
- assertThat(HealthConnectClient.isProviderAvailable(context, PROVIDER_PACKAGE_NAME))
- .isFalse()
assertThat(HealthConnectClient.getSdkStatus(context, PROVIDER_PACKAGE_NAME))
.isEqualTo(HealthConnectClient.SDK_UNAVAILABLE)
assertThrows(UnsupportedOperationException::class.java) {
@@ -140,6 +157,17 @@
}
}
+ @Test
+ @Config(sdk = [Build.VERSION_CODES.O_MR1])
+ @Suppress("Deprecation")
+ fun sdkVersionTooOld_legacyClient_unavailable() {
+ assertThat(HealthConnectClient.getSdkStatusLegacy(context, PROVIDER_PACKAGE_NAME))
+ .isEqualTo(HealthConnectClient.SDK_UNAVAILABLE)
+ assertThrows(UnsupportedOperationException::class.java) {
+ HealthConnectClient.getOrCreateLegacy(context, PROVIDER_PACKAGE_NAME)
+ }
+ }
+
private fun installPackage(
context: Context,
packageName: String,
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/PermissionControllerTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/PermissionControllerTest.kt
index f0d031bc..00482f1 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/PermissionControllerTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/PermissionControllerTest.kt
@@ -17,13 +17,16 @@
package androidx.health.connect.client
import android.content.Context
+import android.os.Build.VERSION_CODES
+import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.health.connect.client.permission.HealthPermission
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
private const val PROVIDER_PACKAGE_NAME = "com.example.fake.provider"
@@ -38,7 +41,7 @@
}
@Test
- fun createIntentTest_permissionStrings() {
+ fun createIntent_permissionStrings() {
val requestPermissionContract =
PermissionController.createRequestPermissionResultContract(PROVIDER_PACKAGE_NAME)
val intent =
@@ -47,7 +50,40 @@
setOf(HealthPermission.READ_ACTIVE_CALORIES_BURNED)
)
- Truth.assertThat(intent.action).isEqualTo("androidx.health.ACTION_REQUEST_PERMISSIONS")
- Truth.assertThat(intent.`package`).isEqualTo(PROVIDER_PACKAGE_NAME)
+ assertThat(intent.action).isEqualTo("androidx.health.ACTION_REQUEST_PERMISSIONS")
+ assertThat(intent.`package`).isEqualTo(PROVIDER_PACKAGE_NAME)
+ }
+
+ @Test
+ @Config(minSdk = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun createIntent_UpsideDownCake() {
+ val requestPermissionContract =
+ PermissionController.createRequestPermissionResultContract(PROVIDER_PACKAGE_NAME)
+ val intent =
+ requestPermissionContract.createIntent(
+ context,
+ setOf(HealthPermission.WRITE_STEPS, HealthPermission.READ_DISTANCE)
+ )
+
+ assertThat(intent.action).isEqualTo(RequestMultiplePermissions.ACTION_REQUEST_PERMISSIONS)
+ assertThat(intent.getStringArrayExtra(RequestMultiplePermissions.EXTRA_PERMISSIONS))
+ .asList()
+ .containsExactly(HealthPermission.WRITE_STEPS, HealthPermission.READ_DISTANCE)
+ assertThat(intent.`package`).isNull()
+ }
+
+ @Test
+ @Config(minSdk = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ fun createIntentLegacy_UpsideDownCake() {
+ val requestPermissionContract =
+ PermissionController.createRequestPermissionResultContractLegacy(PROVIDER_PACKAGE_NAME)
+ val intent =
+ requestPermissionContract.createIntent(
+ context,
+ setOf(HealthPermission.WRITE_STEPS, HealthPermission.READ_DISTANCE)
+ )
+
+ assertThat(intent.action).isEqualTo("androidx.health.ACTION_REQUEST_PERMISSIONS")
+ assertThat(intent.`package`).isEqualTo(PROVIDER_PACKAGE_NAME)
}
}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCakeTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCakeTest.kt
new file mode 100644
index 0000000..2bbd4f4
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/platform/HealthDataRequestPermissionsUpsideDownCakeTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.permission.platform
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
+import androidx.health.connect.client.permission.HealthPermission
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class HealthDataRequestPermissionsUpsideDownCakeTest {
+
+ private lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ }
+
+ @Test
+ fun createIntent() {
+ val requestPermissionContract = HealthDataRequestPermissionsUpsideDownCake()
+ val intent =
+ requestPermissionContract.createIntent(
+ context, setOf(HealthPermission.READ_STEPS, HealthPermission.WRITE_DISTANCE))
+
+ assertThat(intent.action).isEqualTo(RequestMultiplePermissions.ACTION_REQUEST_PERMISSIONS)
+ assertThat(intent.getStringArrayExtra(RequestMultiplePermissions.EXTRA_PERMISSIONS))
+ .asList()
+ .containsExactly(HealthPermission.READ_STEPS, HealthPermission.WRITE_DISTANCE)
+ }
+
+ @Test
+ fun createIntent_nonHealthPermission_throwsIAE() {
+ val requestPermissionContract = HealthDataRequestPermissionsUpsideDownCake()
+ assertFailsWith<IllegalArgumentException> {
+ requestPermissionContract.createIntent(
+ context, setOf(HealthPermission.READ_STEPS, "NON_HEALTH_PERMISSION"))
+ }
+ }
+
+ @Test
+ fun parseIntent() {
+ val requestPermissionContract = HealthDataRequestPermissionsUpsideDownCake()
+
+ val intent = Intent()
+ intent.putExtra(
+ RequestMultiplePermissions.EXTRA_PERMISSIONS,
+ arrayOf(
+ HealthPermission.READ_STEPS,
+ HealthPermission.WRITE_STEPS,
+ HealthPermission.WRITE_DISTANCE,
+ HealthPermission.READ_HEART_RATE))
+ intent.putExtra(
+ RequestMultiplePermissions.EXTRA_PERMISSION_GRANT_RESULTS,
+ intArrayOf(
+ PackageManager.PERMISSION_GRANTED,
+ PackageManager.PERMISSION_DENIED,
+ PackageManager.PERMISSION_GRANTED,
+ PackageManager.PERMISSION_DENIED))
+
+ val result = requestPermissionContract.parseResult(Activity.RESULT_OK, intent)
+
+ assertThat(result)
+ .containsExactly(HealthPermission.READ_STEPS, HealthPermission.WRITE_DISTANCE)
+ }
+}
diff --git a/health/connect/connect-client/src/test/resources/robolectric.properties b/health/connect/connect-client/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/health/connect/connect-client/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/health/health-services-client/src/test/resources/robolectric.properties b/health/health-services-client/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/health/health-services-client/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/heifwriter/heifwriter/api/api_lint.ignore b/heifwriter/heifwriter/api/api_lint.ignore
index a93261a..d3c7e43 100644
--- a/heifwriter/heifwriter/api/api_lint.ignore
+++ b/heifwriter/heifwriter/api/api_lint.ignore
@@ -1,42 +1,24 @@
// Baseline format: 1.0
+GenericException: androidx.heifwriter.AvifWriter#stop(long):
+ Methods must not throw generic exceptions (`java.lang.Exception`)
GenericException: androidx.heifwriter.HeifWriter#stop(long):
Methods must not throw generic exceptions (`java.lang.Exception`)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setGridEnabled(boolean):
- androidx.heifwriter.HeifWriter does not declare a `isGridEnabled()` method matching method androidx.heifwriter.HeifWriter.Builder.setGridEnabled(boolean)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setHandler(android.os.Handler):
- androidx.heifwriter.HeifWriter does not declare a `getHandler()` method matching method androidx.heifwriter.HeifWriter.Builder.setHandler(android.os.Handler)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setMaxImages(int):
- androidx.heifwriter.HeifWriter does not declare a `getMaxImages()` method matching method androidx.heifwriter.HeifWriter.Builder.setMaxImages(int)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setPrimaryIndex(int):
- androidx.heifwriter.HeifWriter does not declare a `getPrimaryIndex()` method matching method androidx.heifwriter.HeifWriter.Builder.setPrimaryIndex(int)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setQuality(int):
- androidx.heifwriter.HeifWriter does not declare a `getQuality()` method matching method androidx.heifwriter.HeifWriter.Builder.setQuality(int)
-MissingGetterMatchingBuilder: androidx.heifwriter.HeifWriter.Builder#setRotation(int):
- androidx.heifwriter.HeifWriter does not declare a `getRotation()` method matching method androidx.heifwriter.HeifWriter.Builder.setRotation(int)
-
-
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#build():
- Missing nullability on method `build` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setGridEnabled(boolean):
- Missing nullability on method `setGridEnabled` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setHandler(android.os.Handler):
- Missing nullability on method `setHandler` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setMaxImages(int):
- Missing nullability on method `setMaxImages` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setPrimaryIndex(int):
- Missing nullability on method `setPrimaryIndex` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setQuality(int):
- Missing nullability on method `setQuality` return
-MissingNullability: androidx.heifwriter.HeifWriter.Builder#setRotation(int):
- Missing nullability on method `setRotation` return
-
-
+UseParcelFileDescriptor: androidx.heifwriter.AvifWriter.Builder#Builder(java.io.FileDescriptor, int, int, int) parameter #0:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.heifwriter.AvifWriter.Builder(java.io.FileDescriptor fd, int width, int height, int inputMode)
UseParcelFileDescriptor: androidx.heifwriter.HeifWriter.Builder#Builder(java.io.FileDescriptor, int, int, int) parameter #0:
Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.heifwriter.HeifWriter.Builder(java.io.FileDescriptor fd, int width, int height, int inputMode)
+VisiblySynchronized: androidx.heifwriter.AvifWriter#addBitmap(android.graphics.Bitmap):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.AvifWriter.addBitmap(android.graphics.Bitmap)
+VisiblySynchronized: androidx.heifwriter.AvifWriter#addYuvBuffer(int, byte[]):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.AvifWriter.addYuvBuffer(int,byte[])
+VisiblySynchronized: androidx.heifwriter.AvifWriter#setInputEndOfStreamTimestamp(long):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.AvifWriter.setInputEndOfStreamTimestamp(long)
+VisiblySynchronized: androidx.heifwriter.AvifWriter#stop(long):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.AvifWriter.stop(long)
VisiblySynchronized: androidx.heifwriter.HeifWriter#addBitmap(android.graphics.Bitmap):
Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.HeifWriter.addBitmap(android.graphics.Bitmap)
VisiblySynchronized: androidx.heifwriter.HeifWriter#addYuvBuffer(int, byte[]):
diff --git a/heifwriter/heifwriter/api/current.txt b/heifwriter/heifwriter/api/current.txt
index ce07b53..f6e78d2 100644
--- a/heifwriter/heifwriter/api/current.txt
+++ b/heifwriter/heifwriter/api/current.txt
@@ -1,30 +1,71 @@
// Signature format: 4.0
package androidx.heifwriter {
+ public final class AvifWriter implements java.lang.AutoCloseable {
+ method public void addBitmap(android.graphics.Bitmap);
+ method public void addExifData(int, byte[], int, int);
+ method public void addYuvBuffer(int, byte[]);
+ method public void close();
+ method public android.os.Handler? getHandler();
+ method public android.view.Surface getInputSurface();
+ method public int getMaxImages();
+ method public int getPrimaryIndex();
+ method public int getQuality();
+ method public int getRotation();
+ method public boolean isGridEnabled();
+ method public boolean isHighBitDepthEnabled();
+ method public void setInputEndOfStreamTimestamp(@IntRange(from=0) long);
+ method public void start();
+ method public void stop(@IntRange(from=0) long) throws java.lang.Exception;
+ field public static final int INPUT_MODE_BITMAP = 2; // 0x2
+ field public static final int INPUT_MODE_BUFFER = 0; // 0x0
+ field public static final int INPUT_MODE_SURFACE = 1; // 0x1
+ }
+
+ public static final class AvifWriter.Builder {
+ ctor public AvifWriter.Builder(java.io.FileDescriptor, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ ctor public AvifWriter.Builder(String, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public androidx.heifwriter.AvifWriter build() throws java.io.IOException;
+ method public androidx.heifwriter.AvifWriter.Builder setGridEnabled(boolean);
+ method public androidx.heifwriter.AvifWriter.Builder setHandler(android.os.Handler?);
+ method public androidx.heifwriter.AvifWriter.Builder setHighBitDepthEnabled(boolean);
+ method public androidx.heifwriter.AvifWriter.Builder setMaxImages(@IntRange(from=1) int);
+ method public androidx.heifwriter.AvifWriter.Builder setPrimaryIndex(@IntRange(from=0) int);
+ method public androidx.heifwriter.AvifWriter.Builder setQuality(@IntRange(from=0, to=100) int);
+ method public androidx.heifwriter.AvifWriter.Builder setRotation(@IntRange(from=0) int);
+ }
+
public final class HeifWriter implements java.lang.AutoCloseable {
method public void addBitmap(android.graphics.Bitmap);
method public void addExifData(int, byte[], int, int);
method public void addYuvBuffer(int, byte[]);
method public void close();
+ method public android.os.Handler? getHandler();
method public android.view.Surface getInputSurface();
- method public void setInputEndOfStreamTimestamp(long);
+ method public int getMaxImages();
+ method public int getPrimaryIndex();
+ method public int getQuality();
+ method public int getRotation();
+ method public boolean isGridEnabled();
+ method public boolean isHighBitDepthEnabled();
+ method public void setInputEndOfStreamTimestamp(@IntRange(from=0) long);
method public void start();
- method public void stop(long) throws java.lang.Exception;
+ method public void stop(@IntRange(from=0) long) throws java.lang.Exception;
field public static final int INPUT_MODE_BITMAP = 2; // 0x2
field public static final int INPUT_MODE_BUFFER = 0; // 0x0
field public static final int INPUT_MODE_SURFACE = 1; // 0x1
}
public static final class HeifWriter.Builder {
- ctor public HeifWriter.Builder(java.io.FileDescriptor, int, int, int);
- ctor public HeifWriter.Builder(String, int, int, int);
- method public androidx.heifwriter.HeifWriter! build() throws java.io.IOException;
- method public androidx.heifwriter.HeifWriter.Builder! setGridEnabled(boolean);
- method public androidx.heifwriter.HeifWriter.Builder! setHandler(android.os.Handler?);
- method public androidx.heifwriter.HeifWriter.Builder! setMaxImages(int);
- method public androidx.heifwriter.HeifWriter.Builder! setPrimaryIndex(int);
- method public androidx.heifwriter.HeifWriter.Builder! setQuality(int);
- method public androidx.heifwriter.HeifWriter.Builder! setRotation(int);
+ ctor public HeifWriter.Builder(java.io.FileDescriptor, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ ctor public HeifWriter.Builder(String, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public androidx.heifwriter.HeifWriter build() throws java.io.IOException;
+ method public androidx.heifwriter.HeifWriter.Builder setGridEnabled(boolean);
+ method public androidx.heifwriter.HeifWriter.Builder setHandler(android.os.Handler?);
+ method public androidx.heifwriter.HeifWriter.Builder setMaxImages(@IntRange(from=1) int);
+ method public androidx.heifwriter.HeifWriter.Builder setPrimaryIndex(@IntRange(from=0) int);
+ method public androidx.heifwriter.HeifWriter.Builder setQuality(@IntRange(from=0, to=100) int);
+ method public androidx.heifwriter.HeifWriter.Builder setRotation(@IntRange(from=0) int);
}
}
diff --git a/heifwriter/heifwriter/api/restricted_current.txt b/heifwriter/heifwriter/api/restricted_current.txt
index ce07b53..f6e78d2 100644
--- a/heifwriter/heifwriter/api/restricted_current.txt
+++ b/heifwriter/heifwriter/api/restricted_current.txt
@@ -1,30 +1,71 @@
// Signature format: 4.0
package androidx.heifwriter {
+ public final class AvifWriter implements java.lang.AutoCloseable {
+ method public void addBitmap(android.graphics.Bitmap);
+ method public void addExifData(int, byte[], int, int);
+ method public void addYuvBuffer(int, byte[]);
+ method public void close();
+ method public android.os.Handler? getHandler();
+ method public android.view.Surface getInputSurface();
+ method public int getMaxImages();
+ method public int getPrimaryIndex();
+ method public int getQuality();
+ method public int getRotation();
+ method public boolean isGridEnabled();
+ method public boolean isHighBitDepthEnabled();
+ method public void setInputEndOfStreamTimestamp(@IntRange(from=0) long);
+ method public void start();
+ method public void stop(@IntRange(from=0) long) throws java.lang.Exception;
+ field public static final int INPUT_MODE_BITMAP = 2; // 0x2
+ field public static final int INPUT_MODE_BUFFER = 0; // 0x0
+ field public static final int INPUT_MODE_SURFACE = 1; // 0x1
+ }
+
+ public static final class AvifWriter.Builder {
+ ctor public AvifWriter.Builder(java.io.FileDescriptor, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ ctor public AvifWriter.Builder(String, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public androidx.heifwriter.AvifWriter build() throws java.io.IOException;
+ method public androidx.heifwriter.AvifWriter.Builder setGridEnabled(boolean);
+ method public androidx.heifwriter.AvifWriter.Builder setHandler(android.os.Handler?);
+ method public androidx.heifwriter.AvifWriter.Builder setHighBitDepthEnabled(boolean);
+ method public androidx.heifwriter.AvifWriter.Builder setMaxImages(@IntRange(from=1) int);
+ method public androidx.heifwriter.AvifWriter.Builder setPrimaryIndex(@IntRange(from=0) int);
+ method public androidx.heifwriter.AvifWriter.Builder setQuality(@IntRange(from=0, to=100) int);
+ method public androidx.heifwriter.AvifWriter.Builder setRotation(@IntRange(from=0) int);
+ }
+
public final class HeifWriter implements java.lang.AutoCloseable {
method public void addBitmap(android.graphics.Bitmap);
method public void addExifData(int, byte[], int, int);
method public void addYuvBuffer(int, byte[]);
method public void close();
+ method public android.os.Handler? getHandler();
method public android.view.Surface getInputSurface();
- method public void setInputEndOfStreamTimestamp(long);
+ method public int getMaxImages();
+ method public int getPrimaryIndex();
+ method public int getQuality();
+ method public int getRotation();
+ method public boolean isGridEnabled();
+ method public boolean isHighBitDepthEnabled();
+ method public void setInputEndOfStreamTimestamp(@IntRange(from=0) long);
method public void start();
- method public void stop(long) throws java.lang.Exception;
+ method public void stop(@IntRange(from=0) long) throws java.lang.Exception;
field public static final int INPUT_MODE_BITMAP = 2; // 0x2
field public static final int INPUT_MODE_BUFFER = 0; // 0x0
field public static final int INPUT_MODE_SURFACE = 1; // 0x1
}
public static final class HeifWriter.Builder {
- ctor public HeifWriter.Builder(java.io.FileDescriptor, int, int, int);
- ctor public HeifWriter.Builder(String, int, int, int);
- method public androidx.heifwriter.HeifWriter! build() throws java.io.IOException;
- method public androidx.heifwriter.HeifWriter.Builder! setGridEnabled(boolean);
- method public androidx.heifwriter.HeifWriter.Builder! setHandler(android.os.Handler?);
- method public androidx.heifwriter.HeifWriter.Builder! setMaxImages(int);
- method public androidx.heifwriter.HeifWriter.Builder! setPrimaryIndex(int);
- method public androidx.heifwriter.HeifWriter.Builder! setQuality(int);
- method public androidx.heifwriter.HeifWriter.Builder! setRotation(int);
+ ctor public HeifWriter.Builder(java.io.FileDescriptor, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ ctor public HeifWriter.Builder(String, @IntRange(from=1) int, @IntRange(from=1) int, int);
+ method public androidx.heifwriter.HeifWriter build() throws java.io.IOException;
+ method public androidx.heifwriter.HeifWriter.Builder setGridEnabled(boolean);
+ method public androidx.heifwriter.HeifWriter.Builder setHandler(android.os.Handler?);
+ method public androidx.heifwriter.HeifWriter.Builder setMaxImages(@IntRange(from=1) int);
+ method public androidx.heifwriter.HeifWriter.Builder setPrimaryIndex(@IntRange(from=0) int);
+ method public androidx.heifwriter.HeifWriter.Builder setQuality(@IntRange(from=0, to=100) int);
+ method public androidx.heifwriter.HeifWriter.Builder setRotation(@IntRange(from=0) int);
}
}
diff --git a/heifwriter/heifwriter/lint-baseline.xml b/heifwriter/heifwriter/lint-baseline.xml
index ad11f86..d1c1def 100644
--- a/heifwriter/heifwriter/lint-baseline.xml
+++ b/heifwriter/heifwriter/lint-baseline.xml
@@ -4,6 +4,33 @@
<issue
id="BanHideAnnotation"
message="@hide is not allowed in Javadoc"
+ errorLine1="public final class AvifEncoder extends EncoderBase {"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/heifwriter/AvifEncoder.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" public @interface InputMode {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/heifwriter/AvifWriter.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1="public class EncoderBase implements AutoCloseable,"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/heifwriter/EncoderBase.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
errorLine1=" public @interface ProgramType {}"
errorLine2=" ~~~~~~~~~~~">
<location
@@ -11,12 +38,30 @@
</issue>
<issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1="public class WriterBase implements AutoCloseable {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/heifwriter/WriterBase.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" public @interface InputMode {}"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/heifwriter/WriterBase.java"/>
+ </issue>
+
+ <issue
id="BanSynchronizedMethods"
message="Use of synchronized methods is not recommended"
errorLine1=" synchronized void updateInputEOSTime(long timestampNs) {"
errorLine2=" ^">
<location
- file="src/main/java/androidx/heifwriter/HeifEncoder.java"/>
+ file="src/main/java/androidx/heifwriter/EncoderBase.java"/>
</issue>
<issue
@@ -25,7 +70,7 @@
errorLine1=" synchronized boolean updateLastInputAndEncoderTime(long inputTimeNs, long encoderTimeUs) {"
errorLine2=" ^">
<location
- file="src/main/java/androidx/heifwriter/HeifEncoder.java"/>
+ file="src/main/java/androidx/heifwriter/EncoderBase.java"/>
</issue>
<issue
@@ -34,7 +79,7 @@
errorLine1=" synchronized void updateLastOutputTime(long outputTimeUs) {"
errorLine2=" ^">
<location
- file="src/main/java/androidx/heifwriter/HeifEncoder.java"/>
+ file="src/main/java/androidx/heifwriter/EncoderBase.java"/>
</issue>
<issue
@@ -43,7 +88,7 @@
errorLine1=" synchronized void waitForResult(long timeoutMs) throws Exception {"
errorLine2=" ^">
<location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
+ file="src/main/java/androidx/heifwriter/WriterBase.java"/>
</issue>
<issue
@@ -52,70 +97,16 @@
errorLine1=" synchronized void signalResult(@Nullable Exception e) {"
errorLine2=" ^">
<location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
+ file="src/main/java/androidx/heifwriter/WriterBase.java"/>
</issue>
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setRotation(int rotation) {"
- errorLine2=" ~~~~~~~">
+ errorLine1=" protected static String findAv1Fallback() {"
+ errorLine2=" ~~~~~~">
<location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setGridEnabled(boolean gridEnabled) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setQuality(int quality) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setMaxImages(int maxImages) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setPrimaryIndex(int primaryIndex) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Builder setHandler(@Nullable Handler handler) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public HeifWriter build() throws IOException {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/heifwriter/HeifWriter.java"/>
+ file="src/main/java/androidx/heifwriter/AvifEncoder.java"/>
</issue>
</issues>
diff --git a/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/AvifWriterTest.java b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/AvifWriterTest.java
new file mode 100644
index 0000000..ee27431
--- /dev/null
+++ b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/AvifWriterTest.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.heifwriter;
+
+import static androidx.heifwriter.AvifWriter.INPUT_MODE_BITMAP;
+import static androidx.heifwriter.AvifWriter.INPUT_MODE_BUFFER;
+import static androidx.heifwriter.AvifWriter.INPUT_MODE_SURFACE;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.Manifest;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+
+import androidx.heifwriter.test.R;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Test {@link AvifWriter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@FlakyTest
+public class AvifWriterTest extends TestBase {
+ private static final String TAG = AvifWriterTest.class.getSimpleName();
+
+ @Rule
+ public GrantPermissionRule mRuntimePermissionRule1 =
+ GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
+
+ @Rule
+ public GrantPermissionRule mRuntimePermissionRule =
+ GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
+ private static final boolean DEBUG = true;
+ private static final boolean DUMP_YUV_INPUT = false;
+
+ private static final String AVIFWRITER_INPUT = "heifwriter_input.heic";
+ private static final int[] IMAGE_RESOURCES = new int[] {
+ R.raw.heifwriter_input
+ };
+ private static final String[] IMAGE_FILENAMES = new String[] {
+ AVIFWRITER_INPUT
+ };
+ private static final String OUTPUT_FILENAME = "output.avif";
+
+ @Before
+ public void setUp() throws Exception {
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String outputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+
+ InputStream inputStream = null;
+ FileOutputStream outputStream = null;
+ try {
+ inputStream = getApplicationContext()
+ .getResources().openRawResource(IMAGE_RESOURCES[i]);
+ outputStream = new FileOutputStream(outputPath);
+ copy(inputStream, outputStream);
+ } finally {
+ closeQuietly(inputStream);
+ closeQuietly(outputStream);
+ }
+ }
+
+ HandlerThread handlerThread = new HandlerThread(
+ "AvifEncoderThread", Process.THREAD_PRIORITY_FOREGROUND);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String imageFilePath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+ File imageFile = new File(imageFilePath);
+ if (imageFile.exists()) {
+ imageFile.delete();
+ }
+ }
+ }
+
+ @Test
+ @LargeTest
+ public void testInputBuffer_NoGrid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, false, false, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @Test
+ @LargeTest
+ public void testInputBuffer_Grid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, true, false, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @Test
+ @LargeTest
+ public void testInputBuffer_NoGrid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, false, true, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @Test
+ @LargeTest
+ public void testInputBuffer_Grid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, true, true, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputSurface_NoGrid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, false, false, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+ //
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputSurface_Grid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, true, false, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputSurface_NoGrid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, false, true, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputSurface_Grid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, true, true, OUTPUT_FILENAME);
+ doTestForVariousNumberImages(builder);
+ }
+
+
+ @Test
+ @LargeTest
+ public void testInputBitmap_NoGrid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, false, false, OUTPUT_FILENAME);
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+ doTestForVariousNumberImages(builder.setInputPath(inputPath));
+ }
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputBitmap_Grid_NoHandler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, true, false, OUTPUT_FILENAME);
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+ doTestForVariousNumberImages(builder.setInputPath(inputPath));
+ }
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputBitmap_NoGrid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, false, true, OUTPUT_FILENAME);
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+ doTestForVariousNumberImages(builder.setInputPath(inputPath));
+ }
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @LargeTest
+ public void testInputBitmap_Grid_Handler() throws Throwable {
+ if (shouldSkip()) return;
+
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, true, true, OUTPUT_FILENAME);
+ for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+ String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ IMAGE_FILENAMES[i]).getAbsolutePath();
+ doTestForVariousNumberImages(builder.setInputPath(inputPath));
+ }
+ }
+
+ @SdkSuppress(maxSdkVersion = 29) // b/192261638
+ @Test
+ @SmallTest
+ public void testCloseWithoutStart() throws Throwable {
+ if (shouldSkip()) return;
+
+ final String outputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ OUTPUT_FILENAME).getAbsolutePath();
+ AvifWriter avifWriter = new AvifWriter.Builder(
+ outputPath, 1920, 1080, INPUT_MODE_SURFACE)
+ .setGridEnabled(true)
+ .setMaxImages(4)
+ .setQuality(90)
+ .setPrimaryIndex(0)
+ .setHandler(mHandler)
+ .build();
+
+ avifWriter.close();
+ }
+
+ private void doTestForVariousNumberImages(TestConfig.Builder builder) throws Exception {
+ builder.setHighBitDepthEnabled(false);
+ builder.setNumImages(4);
+ doTest(builder.setRotation(270).build());
+ doTest(builder.setRotation(180).build());
+ doTest(builder.setRotation(90).build());
+ doTest(builder.setRotation(0).build());
+ doTest(builder.setNumImages(1).build());
+ doTest(builder.setNumImages(8).build());
+
+ builder.setHighBitDepthEnabled(true);
+ builder.setNumImages(1);
+ doTest(builder.setRotation(270).build());
+ doTest(builder.setRotation(180).build());
+ doTest(builder.setRotation(90).build());
+ doTest(builder.setRotation(0).build());
+ doTest(builder.setNumImages(1).build());
+ doTest(builder.setNumImages(8).build());
+ }
+
+ private boolean shouldSkip() {
+ return !hasEncoderForMime(MediaFormat.MIMETYPE_VIDEO_AV1);
+ }
+
+ private static byte[] mYuvData;
+ private void doTest(final TestConfig config) throws Exception {
+ final int width = config.mWidth;
+ final int height = config.mHeight;
+ final int actualNumImages = config.mActualNumImages;
+
+ mInputIndex = 0;
+ AvifWriter avifWriter = null;
+ FileInputStream inputStream = null;
+ FileOutputStream outputStream = null;
+ String outputFileName;
+ try {
+ if (DEBUG)
+ Log.d(TAG, "started: " + config);
+ outputFileName = new File(getApplicationContext().getExternalFilesDir(null),
+ OUTPUT_FILENAME).getAbsolutePath();
+
+ if(!config.mUseHighBitDepth){
+ avifWriter =
+ new AvifWriter.Builder(outputFileName, width, height, config.mInputMode)
+ .setRotation(config.mRotation)
+ .setGridEnabled(config.mUseGrid)
+ .setMaxImages(config.mMaxNumImages)
+ .setQuality(config.mQuality)
+ .setPrimaryIndex(config.mMaxNumImages - 1)
+ .setHandler(config.mUseHandler ? mHandler : null)
+ .build();
+ } else {
+ avifWriter =
+ new AvifWriter.Builder(outputFileName, width, height, config.mInputMode)
+ .setRotation(config.mRotation)
+ .setGridEnabled(config.mUseGrid)
+ .setMaxImages(config.mMaxNumImages)
+ .setQuality(config.mQuality)
+ .setPrimaryIndex(config.mMaxNumImages - 1)
+ .setHandler(config.mUseHandler ? mHandler : null)
+ .setHighBitDepthEnabled(true)
+ .build();
+ }
+
+ if (config.mInputMode == INPUT_MODE_SURFACE) {
+ mInputEglSurface = new EglWindowSurface(avifWriter.getInputSurface());
+ }
+
+ avifWriter.start();
+
+ if (config.mInputMode == INPUT_MODE_BUFFER) {
+ if (!config.mUseHighBitDepth) {
+ if (mYuvData == null || mYuvData.length != width * height * 3 / 2) {
+ mYuvData = new byte[width * height * 3 / 2];
+ }
+ } else {
+ if (mYuvData == null || mYuvData.length != width * height * 3) {
+ mYuvData = new byte[width * height * 3];
+ }
+ }
+
+ if (config.mInputPath != null) {
+ inputStream = new FileInputStream(config.mInputPath);
+ }
+
+ if (DUMP_YUV_INPUT) {
+ File outputFile = new File("/sdcard/input.yuv");
+ outputFile.createNewFile();
+ outputStream = new FileOutputStream(outputFile);
+ }
+
+ for (int i = 0; i < actualNumImages; i++) {
+ if (DEBUG)
+ Log.d(TAG, "fillYuvBuffer: " + i);
+ fillYuvBuffer(i, mYuvData, width, height, inputStream);
+ if (DUMP_YUV_INPUT) {
+ Log.d(TAG, "@@@ dumping input YUV");
+ outputStream.write(mYuvData);
+ }
+ if (!config.mUseHighBitDepth) {
+ avifWriter.addYuvBuffer(ImageFormat.YUV_420_888, mYuvData);
+ } else {
+ avifWriter.addYuvBuffer(ImageFormat.YCBCR_P010, mYuvData);
+ }
+ }
+ } else if (config.mInputMode == INPUT_MODE_SURFACE) {
+ // The input surface is a surface texture using single buffer mode, draws will be
+ // blocked until onFrameAvailable is done with the buffer, which is dependant on
+ // how fast MediaCodec processes them, which is further dependent on how fast the
+ // MediaCodec callbacks are handled. We can't put draws on the same looper that
+ // handles MediaCodec callback, it will cause deadlock.
+ for (int i = 0; i < actualNumImages; i++) {
+ if (DEBUG)
+ Log.d(TAG, "drawFrame: " + i);
+ drawFrame(width, height);
+ }
+ avifWriter.setInputEndOfStreamTimestamp(
+ 1000 * computePresentationTime(actualNumImages - 1));
+ } else if (config.mInputMode == INPUT_MODE_BITMAP) {
+ if(!config.mUseHighBitDepth) {
+ Bitmap[] bitmaps = config.mBitmaps;
+ for (int i = 0; i < Math.min(bitmaps.length, actualNumImages); i++) {
+ if (DEBUG) {
+ Log.d(TAG, "addBitmap: " + i);
+ }
+ avifWriter.addBitmap(bitmaps[i]);
+ bitmaps[i].recycle();
+ }
+ } else {
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inPreferredConfig = Bitmap.Config.RGBA_F16;
+ InputStream inputStream10Bit = getApplicationContext().getResources()
+ .openRawResource(R.raw.heifwriter_input10);
+ Bitmap bm = BitmapFactory.decodeStream(inputStream10Bit, null, opt);
+ assertNotNull(bm);
+ avifWriter.addBitmap(bm);
+ bm.recycle();
+ }
+ }
+
+ avifWriter.stop(10000);
+ // The test sets the primary index to the last image.
+ // However, if we're testing early abort, the last image will not be
+ // present and the muxer is supposed to set it to 0 by default.
+ int expectedPrimary = config.mMaxNumImages - 1;
+ int expectedImageCount = config.mMaxNumImages;
+ if (actualNumImages < config.mMaxNumImages) {
+ expectedPrimary = 0;
+ expectedImageCount = actualNumImages;
+ }
+ verifyResult(config.mOutputPath, width, height, config.mRotation,
+ expectedImageCount, expectedPrimary, config.mUseGrid,
+ config.mInputMode == INPUT_MODE_SURFACE);
+ if (DEBUG)
+ Log.d(TAG, "finished: PASS");
+ } finally {
+ try {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ } catch (IOException e) {
+ }
+
+ if (avifWriter != null) {
+ avifWriter.close();
+ avifWriter = null;
+ }
+ if (mInputEglSurface != null) {
+ // This also releases the surface from encoder.
+ mInputEglSurface.release();
+ mInputEglSurface = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
index b8e3752..a536c7f 100644
--- a/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
+++ b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
@@ -21,28 +21,15 @@
import static androidx.heifwriter.HeifWriter.INPUT_MODE_SURFACE;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import android.Manifest;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
-import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.media.MediaMetadataRetriever;
-import android.opengl.GLES20;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.heifwriter.test.R;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
@@ -58,80 +45,52 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Arrays;
/**
* Test {@link HeifWriter}.
*/
@RunWith(AndroidJUnit4.class)
@FlakyTest
-public class HeifWriterTest {
+public class HeifWriterTest extends TestBase {
private static final String TAG = HeifWriterTest.class.getSimpleName();
- private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-
@Rule
public GrantPermissionRule mRuntimePermissionRule1 =
- GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
+ GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
@Rule
public GrantPermissionRule mRuntimePermissionRule =
- GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final boolean DUMP_YUV_INPUT = false;
- private static final byte[][] TEST_YUV_COLORS = {
- {(byte) 255, (byte) 0, (byte) 0},
- {(byte) 255, (byte) 0, (byte) 255},
- {(byte) 255, (byte) 255, (byte) 255},
- {(byte) 255, (byte) 255, (byte) 0},
- };
- private static final Color COLOR_BLOCK =
- Color.valueOf(1.0f, 1.0f, 1.0f);
- private static final Color[] COLOR_BARS = {
- Color.valueOf(0.0f, 0.0f, 0.0f),
- Color.valueOf(0.0f, 0.0f, 0.64f),
- Color.valueOf(0.0f, 0.64f, 0.0f),
- Color.valueOf(0.0f, 0.64f, 0.64f),
- Color.valueOf(0.64f, 0.0f, 0.0f),
- Color.valueOf(0.64f, 0.0f, 0.64f),
- Color.valueOf(0.64f, 0.64f, 0.0f),
- };
- private static final float MAX_DELTA = 0.025f;
- private static final int BORDER_WIDTH = 16;
-
private static final String HEIFWRITER_INPUT = "heifwriter_input.heic";
private static final int[] IMAGE_RESOURCES = new int[] {
- R.raw.heifwriter_input
+ R.raw.heifwriter_input
};
private static final String[] IMAGE_FILENAMES = new String[] {
- HEIFWRITER_INPUT
+ HEIFWRITER_INPUT
};
private static final String OUTPUT_FILENAME = "output.heic";
- private EglWindowSurface mInputEglSurface;
- private Handler mHandler;
- private int mInputIndex;
-
@Before
public void setUp() throws Exception {
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String outputPath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = getApplicationContext()
- .getResources().openRawResource(IMAGE_RESOURCES[i]);
+ .getResources().openRawResource(IMAGE_RESOURCES[i]);
outputStream = new FileOutputStream(outputPath);
copy(inputStream, outputStream);
} finally {
@@ -141,7 +100,7 @@
}
HandlerThread handlerThread = new HandlerThread(
- "HeifEncoderThread", Process.THREAD_PRIORITY_FOREGROUND);
+ "HeifEncoderThread", Process.THREAD_PRIORITY_FOREGROUND);
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
}
@@ -150,7 +109,7 @@
public void tearDown() throws Exception {
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String imageFilePath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
File imageFile = new File(imageFilePath);
if (imageFile.exists()) {
imageFile.delete();
@@ -164,7 +123,8 @@
public void testInputBuffer_NoGrid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, false, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, false, false, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -174,7 +134,8 @@
public void testInputBuffer_Grid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, true, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, true, false, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -184,7 +145,8 @@
public void testInputBuffer_NoGrid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, false, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, false, true, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -194,7 +156,8 @@
public void testInputBuffer_Grid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, true, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BUFFER, true, true, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -204,17 +167,19 @@
public void testInputSurface_NoGrid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, false, false, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
-
+ //
@SdkSuppress(maxSdkVersion = 29) // b/192261638
@Test
@LargeTest
public void testInputSurface_Grid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, true, false, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -224,7 +189,8 @@
public void testInputSurface_NoGrid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, false, true, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
@@ -234,20 +200,23 @@
public void testInputSurface_Grid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_SURFACE, true, true, OUTPUT_FILENAME);
doTestForVariousNumberImages(builder);
}
+
@SdkSuppress(maxSdkVersion = 29) // b/192261638
@Test
@LargeTest
public void testInputBitmap_NoGrid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, false, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, false, false, OUTPUT_FILENAME);
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
doTestForVariousNumberImages(builder.setInputPath(inputPath));
}
}
@@ -258,10 +227,11 @@
public void testInputBitmap_Grid_NoHandler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, true, false);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, true, false, OUTPUT_FILENAME);
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
doTestForVariousNumberImages(builder.setInputPath(inputPath));
}
}
@@ -272,10 +242,11 @@
public void testInputBitmap_NoGrid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, false, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, false, true, OUTPUT_FILENAME);
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
doTestForVariousNumberImages(builder.setInputPath(inputPath));
}
}
@@ -286,10 +257,11 @@
public void testInputBitmap_Grid_Handler() throws Throwable {
if (shouldSkip()) return;
- TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, true, true);
+ TestConfig.Builder builder =
+ new TestConfig.Builder(INPUT_MODE_BITMAP, true, true, OUTPUT_FILENAME);
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String inputPath = new File(getApplicationContext().getExternalFilesDir(null),
- IMAGE_FILENAMES[i]).getAbsolutePath();
+ IMAGE_FILENAMES[i]).getAbsolutePath();
doTestForVariousNumberImages(builder.setInputPath(inputPath));
}
}
@@ -301,15 +273,15 @@
if (shouldSkip()) return;
final String outputPath = new File(getApplicationContext().getExternalFilesDir(null),
- OUTPUT_FILENAME).getAbsolutePath();
+ OUTPUT_FILENAME).getAbsolutePath();
HeifWriter heifWriter = new HeifWriter.Builder(
- outputPath, 1920, 1080, INPUT_MODE_SURFACE)
- .setGridEnabled(true)
- .setMaxImages(4)
- .setQuality(90)
- .setPrimaryIndex(0)
- .setHandler(mHandler)
- .build();
+ outputPath, 1920, 1080, INPUT_MODE_SURFACE)
+ .setGridEnabled(true)
+ .setMaxImages(4)
+ .setQuality(90)
+ .setPrimaryIndex(0)
+ .setHandler(mHandler)
+ .build();
heifWriter.close();
}
@@ -324,186 +296,11 @@
doTest(builder.setNumImages(8).build());
}
- private void closeQuietly(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (RuntimeException rethrown) {
- throw rethrown;
- } catch (Exception ignored) {
- }
- }
- }
-
- private int copy(InputStream in, OutputStream out) throws IOException {
- int total = 0;
- byte[] buffer = new byte[8192];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
- }
- return total;
- }
-
private boolean shouldSkip() {
return !hasEncoderForMime(MediaFormat.MIMETYPE_VIDEO_HEVC)
&& !hasEncoderForMime(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
}
- private boolean hasEncoderForMime(String mime) {
- for (MediaCodecInfo info : sMCL.getCodecInfos()) {
- if (info.isEncoder()) {
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mime)) {
- Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private static class TestConfig {
- final int mInputMode;
- final boolean mUseGrid;
- final boolean mUseHandler;
- final int mMaxNumImages;
- final int mActualNumImages;
- final int mWidth;
- final int mHeight;
- final int mRotation;
- final int mQuality;
- final String mInputPath;
- final String mOutputPath;
- final Bitmap[] mBitmaps;
-
- TestConfig(int inputMode, boolean useGrid, boolean useHandler,
- int maxNumImages, int actualNumImages, int width, int height,
- int rotation, int quality,
- String inputPath, String outputPath, Bitmap[] bitmaps) {
- mInputMode = inputMode;
- mUseGrid = useGrid;
- mUseHandler = useHandler;
- mMaxNumImages = maxNumImages;
- mActualNumImages = actualNumImages;
- mWidth = width;
- mHeight = height;
- mRotation = rotation;
- mQuality = quality;
- mInputPath = inputPath;
- mOutputPath = outputPath;
- mBitmaps = bitmaps;
- }
-
- static class Builder {
- final int mInputMode;
- final boolean mUseGrid;
- final boolean mUseHandler;
- int mMaxNumImages;
- int mNumImages;
- int mWidth;
- int mHeight;
- int mRotation;
- final int mQuality;
- String mInputPath;
- final String mOutputPath;
- Bitmap[] mBitmaps;
- boolean mNumImagesSetExplicitly;
-
-
- Builder(int inputMode, boolean useGrids, boolean useHandler) {
- mInputMode = inputMode;
- mUseGrid = useGrids;
- mUseHandler = useHandler;
- mMaxNumImages = mNumImages = 4;
- mWidth = 1920;
- mHeight = 1080;
- mRotation = 0;
- mQuality = 100;
- mOutputPath = new File(getApplicationContext().getExternalFilesDir(null),
- OUTPUT_FILENAME).getAbsolutePath();
- }
-
- Builder setInputPath(String inputPath) {
- mInputPath = (mInputMode == INPUT_MODE_BITMAP) ? inputPath : null;
- return this;
- }
-
- Builder setNumImages(int numImages) {
- mNumImagesSetExplicitly = true;
- mNumImages = numImages;
- return this;
- }
-
- Builder setRotation(int rotation) {
- mRotation = rotation;
- return this;
- }
-
- private void loadBitmapInputs() {
- if (mInputMode != INPUT_MODE_BITMAP) {
- return;
- }
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- retriever.setDataSource(mInputPath);
- String hasImage = retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
- if (!"yes".equals(hasImage)) {
- throw new IllegalArgumentException("no bitmap found!");
- }
- mMaxNumImages = Math.min(mMaxNumImages, Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
- if (!mNumImagesSetExplicitly) {
- mNumImages = mMaxNumImages;
- }
- mBitmaps = new Bitmap[mMaxNumImages];
- for (int i = 0; i < mBitmaps.length; i++) {
- mBitmaps[i] = retriever.getImageAtIndex(i);
- }
- mWidth = mBitmaps[0].getWidth();
- mHeight = mBitmaps[0].getHeight();
- try {
- retriever.release();
- } catch (IOException e) {
- // Nothing we can do about it.
- }
- }
-
- private void cleanupStaleOutputs() {
- File outputFile = new File(mOutputPath);
- if (outputFile.exists()) {
- outputFile.delete();
- }
- }
-
- TestConfig build() {
- cleanupStaleOutputs();
- loadBitmapInputs();
-
- return new TestConfig(mInputMode, mUseGrid, mUseHandler, mMaxNumImages, mNumImages,
- mWidth, mHeight, mRotation, mQuality, mInputPath, mOutputPath, mBitmaps);
- }
- }
-
- @Override
- public String toString() {
- return "TestConfig"
- + ": mInputMode " + mInputMode
- + ", mUseGrid " + mUseGrid
- + ", mUseHandler " + mUseHandler
- + ", mMaxNumImages " + mMaxNumImages
- + ", mNumImages " + mActualNumImages
- + ", mWidth " + mWidth
- + ", mHeight " + mHeight
- + ", mRotation " + mRotation
- + ", mQuality " + mQuality
- + ", mInputPath " + mInputPath
- + ", mOutputPath " + mOutputPath;
- }
- }
-
private static byte[] mYuvData;
private void doTest(final TestConfig config) throws Exception {
final int width = config.mWidth;
@@ -515,17 +312,19 @@
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
- if (DEBUG) Log.d(TAG, "started: " + config);
+ if (DEBUG)
+ Log.d(TAG, "started: " + config);
heifWriter = new HeifWriter.Builder(
- config.mOutputPath, width, height, config.mInputMode)
- .setRotation(config.mRotation)
- .setGridEnabled(config.mUseGrid)
- .setMaxImages(config.mMaxNumImages)
- .setQuality(config.mQuality)
- .setPrimaryIndex(config.mMaxNumImages - 1)
- .setHandler(config.mUseHandler ? mHandler : null)
- .build();
+ new File(getApplicationContext().getExternalFilesDir(null),
+ OUTPUT_FILENAME).getAbsolutePath(), width, height, config.mInputMode)
+ .setRotation(config.mRotation)
+ .setGridEnabled(config.mUseGrid)
+ .setMaxImages(config.mMaxNumImages)
+ .setQuality(config.mQuality)
+ .setPrimaryIndex(config.mMaxNumImages - 1)
+ .setHandler(config.mUseHandler ? mHandler : null)
+ .build();
if (config.mInputMode == INPUT_MODE_SURFACE) {
mInputEglSurface = new EglWindowSurface(heifWriter.getInputSurface());
@@ -549,7 +348,8 @@
}
for (int i = 0; i < actualNumImages; i++) {
- if (DEBUG) Log.d(TAG, "fillYuvBuffer: " + i);
+ if (DEBUG)
+ Log.d(TAG, "fillYuvBuffer: " + i);
fillYuvBuffer(i, mYuvData, width, height, inputStream);
if (DUMP_YUV_INPUT) {
Log.d(TAG, "@@@ dumping input YUV");
@@ -564,15 +364,17 @@
// MediaCodec callbacks are handled. We can't put draws on the same looper that
// handles MediaCodec callback, it will cause deadlock.
for (int i = 0; i < actualNumImages; i++) {
- if (DEBUG) Log.d(TAG, "drawFrame: " + i);
+ if (DEBUG)
+ Log.d(TAG, "drawFrame: " + i);
drawFrame(width, height);
}
heifWriter.setInputEndOfStreamTimestamp(
- 1000 * computePresentationTime(actualNumImages - 1));
+ 1000 * computePresentationTime(actualNumImages - 1));
} else if (config.mInputMode == INPUT_MODE_BITMAP) {
Bitmap[] bitmaps = config.mBitmaps;
for (int i = 0; i < Math.min(bitmaps.length, actualNumImages); i++) {
- if (DEBUG) Log.d(TAG, "addBitmap: " + i);
+ if (DEBUG)
+ Log.d(TAG, "addBitmap: " + i);
heifWriter.addBitmap(bitmaps[i]);
bitmaps[i].recycle();
}
@@ -589,9 +391,10 @@
expectedImageCount = actualNumImages;
}
verifyResult(config.mOutputPath, width, height, config.mRotation,
- expectedImageCount, expectedPrimary, config.mUseGrid,
- config.mInputMode == INPUT_MODE_SURFACE);
- if (DEBUG) Log.d(TAG, "finished: PASS");
+ expectedImageCount, expectedPrimary, config.mUseGrid,
+ config.mInputMode == INPUT_MODE_SURFACE);
+ if (DEBUG)
+ Log.d(TAG, "finished: PASS");
} finally {
try {
if (outputStream != null) {
@@ -600,7 +403,8 @@
if (inputStream != null) {
inputStream.close();
}
- } catch (IOException e) {}
+ } catch (IOException e) {
+ }
if (heifWriter != null) {
heifWriter.close();
@@ -613,139 +417,4 @@
}
}
}
-
- private long computePresentationTime(int frameIndex) {
- return 132 + (long)frameIndex * 1000000;
- }
-
- private void fillYuvBuffer(int frameIndex, @NonNull byte[] data, int width, int height,
- @Nullable FileInputStream inputStream) throws IOException {
- if (inputStream != null) {
- inputStream.read(data);
- } else {
- byte[] color = TEST_YUV_COLORS[frameIndex % TEST_YUV_COLORS.length];
- int sizeY = width * height;
- Arrays.fill(data, 0, sizeY, color[0]);
- Arrays.fill(data, sizeY, sizeY * 5 / 4, color[1]);
- Arrays.fill(data, sizeY * 5 / 4, sizeY * 3 / 2, color[2]);
- }
- }
-
- private void drawFrame(int width, int height) {
- mInputEglSurface.makeCurrent();
- generateSurfaceFrame(mInputIndex, width, height);
- mInputEglSurface.setPresentationTime(1000 * computePresentationTime(mInputIndex));
- mInputEglSurface.swapBuffers();
- mInputIndex++;
- }
-
- private static Rect getColorBarRect(int index, int width, int height) {
- int barWidth = (width - BORDER_WIDTH * 2) / COLOR_BARS.length;
- return new Rect(BORDER_WIDTH + barWidth * index, BORDER_WIDTH,
- BORDER_WIDTH + barWidth * (index + 1), height - BORDER_WIDTH);
- }
-
- private static Rect getColorBlockRect(int index, int width, int height) {
- int blockCenterX = (width / 5) * (index % 4 + 1);
- return new Rect(blockCenterX - width / 10, height / 6,
- blockCenterX + width / 10, height / 3);
- }
-
- private void generateSurfaceFrame(int frameIndex, int width, int height) {
- GLES20.glViewport(0, 0, width, height);
- GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
- GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
-
- for (int i = 0; i < COLOR_BARS.length; i++) {
- Rect r = getColorBarRect(i, width, height);
-
- GLES20.glScissor(r.left, r.top, r.width(), r.height());
- final Color color = COLOR_BARS[i];
- GLES20.glClearColor(color.red(), color.green(), color.blue(), 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- }
-
- Rect r = getColorBlockRect(frameIndex, width, height);
- GLES20.glScissor(r.left, r.top, r.width(), r.height());
- GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- r.inset(BORDER_WIDTH, BORDER_WIDTH);
- GLES20.glScissor(r.left, r.top, r.width(), r.height());
- GLES20.glClearColor(COLOR_BLOCK.red(), COLOR_BLOCK.green(), COLOR_BLOCK.blue(), 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- }
-
- /**
- * Determines if two color values are approximately equal.
- */
- private static boolean approxEquals(Color expected, Color actual) {
- return (Math.abs(expected.red() - actual.red()) <= MAX_DELTA)
- && (Math.abs(expected.green() - actual.green()) <= MAX_DELTA)
- && (Math.abs(expected.blue() - actual.blue()) <= MAX_DELTA);
- }
-
- private void verifyResult(
- String filename, int width, int height, int rotation,
- int imageCount, int primary, boolean useGrid, boolean checkColor)
- throws Exception {
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- retriever.setDataSource(filename);
- String hasImage = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
- if (!"yes".equals(hasImage)) {
- throw new Exception("No images found in file " + filename);
- }
- assertEquals("Wrong width", width,
- Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
- assertEquals("Wrong height", height,
- Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
- assertEquals("Wrong rotation", rotation,
- Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
- assertEquals("Wrong image count", imageCount,
- Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
- assertEquals("Wrong primary index", primary,
- Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_PRIMARY)));
- try {
- retriever.release();
- } catch (IOException e) {
- // Nothing we can do about it.
- }
-
- if (useGrid) {
- MediaExtractor extractor = new MediaExtractor();
- extractor.setDataSource(filename);
- MediaFormat format = extractor.getTrackFormat(0);
- int tileWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
- int tileHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
- int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
- int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
- assertTrue("Wrong tile width or grid cols",
- ((width + tileWidth - 1) / tileWidth) == gridCols);
- assertTrue("Wrong tile height or grid rows",
- ((height + tileHeight - 1) / tileHeight) == gridRows);
- extractor.release();
- }
-
- if (checkColor) {
- Bitmap bitmap = BitmapFactory.decodeFile(filename);
-
- for (int i = 0; i < COLOR_BARS.length; i++) {
- Rect r = getColorBarRect(i, width, height);
- assertTrue("Color bar " + i + " doesn't match", approxEquals(COLOR_BARS[i],
- Color.valueOf(bitmap.getPixel(r.centerX(), r.centerY()))));
- }
-
- Rect r = getColorBlockRect(primary, width, height);
- assertTrue("Color block doesn't match", approxEquals(COLOR_BLOCK,
- Color.valueOf(bitmap.getPixel(r.centerX(), height - r.centerY()))));
-
- bitmap.recycle();
- }
- }
-}
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/TestBase.java b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/TestBase.java
new file mode 100644
index 0000000..39be1ea
--- /dev/null
+++ b/heifwriter/heifwriter/src/androidTest/java/androidx/heifwriter/TestBase.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.heifwriter;
+
+import static androidx.heifwriter.HeifWriter.INPUT_MODE_BITMAP;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.opengl.GLES20;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * Base class holding common utilities for {@link HeifWriterTest} and {@link AvifWriterTest}.
+ */
+public class TestBase {
+ private static final String TAG = HeifWriterTest.class.getSimpleName();
+
+ private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+
+ private static final byte[][] TEST_YUV_COLORS = {
+ {(byte) 255, (byte) 0, (byte) 0},
+ {(byte) 255, (byte) 0, (byte) 255},
+ {(byte) 255, (byte) 255, (byte) 255},
+ {(byte) 255, (byte) 255, (byte) 0},
+ };
+ private static final byte[][] TEST_YUV_10BIT_COLORS = {
+ {(byte) 1023, (byte) 0, (byte) 0},
+ {(byte) 1023, (byte) 0, (byte) 1023},
+ {(byte) 1023, (byte) 1023, (byte) 1023},
+ {(byte) 1023, (byte) 1023, (byte) 0},
+ };
+ private static final Color COLOR_BLOCK =
+ Color.valueOf(1.0f, 1.0f, 1.0f);
+ private static final Color[] COLOR_BARS = {
+ Color.valueOf(0.0f, 0.0f, 0.0f),
+ Color.valueOf(0.0f, 0.0f, 0.64f),
+ Color.valueOf(0.0f, 0.64f, 0.0f),
+ Color.valueOf(0.0f, 0.64f, 0.64f),
+ Color.valueOf(0.64f, 0.0f, 0.0f),
+ Color.valueOf(0.64f, 0.0f, 0.64f),
+ Color.valueOf(0.64f, 0.64f, 0.0f),
+ };
+ private static final float MAX_DELTA = 0.025f;
+ private static final int BORDER_WIDTH = 16;
+
+ protected EglWindowSurface mInputEglSurface;
+ protected Handler mHandler;
+ protected int mInputIndex;
+ protected boolean mHighBitDepthEnabled = false;
+
+ protected long computePresentationTime(int frameIndex) {
+ return 132 + (long)frameIndex * 1000000;
+ }
+
+ protected void fillYuvBuffer(int frameIndex, @NonNull byte[] data, int width, int height,
+ @Nullable FileInputStream inputStream) throws IOException {
+ if (inputStream != null) {
+ inputStream.read(data);
+ } else {
+ byte[] color;
+ int sizeY = width * height;
+ if (!mHighBitDepthEnabled) {
+ color = TEST_YUV_COLORS[frameIndex % TEST_YUV_COLORS.length];
+ Arrays.fill(data, 0, sizeY, color[0]);
+ Arrays.fill(data, sizeY, sizeY * 5 / 4, color[1]);
+ Arrays.fill(data, sizeY * 5 / 4, sizeY * 3 / 2, color[2]);
+
+ } else {
+ color = TEST_YUV_10BIT_COLORS[frameIndex % TEST_YUV_10BIT_COLORS.length];
+ Arrays.fill(data, 0, sizeY, color[0]);
+ Arrays.fill(data, sizeY, sizeY * 2, color[1]);
+ Arrays.fill(data, sizeY * 2, sizeY * 3, color[2]);
+ }
+ }
+ }
+
+ protected static Rect getColorBarRect(int index, int width, int height) {
+ int barWidth = (width - BORDER_WIDTH * 2) / COLOR_BARS.length;
+ return new Rect(BORDER_WIDTH + barWidth * index, BORDER_WIDTH,
+ BORDER_WIDTH + barWidth * (index + 1), height - BORDER_WIDTH);
+ }
+
+ protected static Rect getColorBlockRect(int index, int width, int height) {
+ int blockCenterX = (width / 5) * (index % 4 + 1);
+ return new Rect(blockCenterX - width / 10, height / 6,
+ blockCenterX + width / 10, height / 3);
+ }
+
+ protected void generateSurfaceFrame(int frameIndex, int width, int height) {
+ GLES20.glViewport(0, 0, width, height);
+ GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
+ GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
+
+ for (int i = 0; i < COLOR_BARS.length; i++) {
+ Rect r = getColorBarRect(i, width, height);
+
+ GLES20.glScissor(r.left, r.top, r.width(), r.height());
+ final Color color = COLOR_BARS[i];
+ GLES20.glClearColor(color.red(), color.green(), color.blue(), 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ }
+
+ Rect r = getColorBlockRect(frameIndex, width, height);
+ GLES20.glScissor(r.left, r.top, r.width(), r.height());
+ GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ r.inset(BORDER_WIDTH, BORDER_WIDTH);
+ GLES20.glScissor(r.left, r.top, r.width(), r.height());
+ GLES20.glClearColor(COLOR_BLOCK.red(), COLOR_BLOCK.green(), COLOR_BLOCK.blue(), 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ }
+
+ /**
+ * Determines if two color values are approximately equal.
+ */
+ protected static boolean approxEquals(Color expected, Color actual) {
+ return (Math.abs(expected.red() - actual.red()) <= MAX_DELTA)
+ && (Math.abs(expected.green() - actual.green()) <= MAX_DELTA)
+ && (Math.abs(expected.blue() - actual.blue()) <= MAX_DELTA);
+ }
+
+ protected void verifyResult(
+ String filename, int width, int height, int rotation,
+ int imageCount, int primary, boolean useGrid, boolean checkColor)
+ throws Exception {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(filename);
+ String hasImage = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
+ if (!"yes".equals(hasImage)) {
+ throw new Exception("No images found in file " + filename);
+ }
+ assertEquals("Wrong width", width,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
+ assertEquals("Wrong height", height,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+ assertEquals("Wrong rotation", rotation,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
+ assertEquals("Wrong image count", imageCount,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
+ assertEquals("Wrong primary index", primary,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_PRIMARY)));
+ try {
+ retriever.release();
+ } catch (IOException e) {
+ // Nothing we can do about it.
+ }
+
+ if (useGrid) {
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(filename);
+ MediaFormat format = extractor.getTrackFormat(0);
+ int tileWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
+ int tileHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
+ int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
+ int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
+ assertTrue("Wrong tile width or grid cols",
+ ((width + tileWidth - 1) / tileWidth) == gridCols);
+ assertTrue("Wrong tile height or grid rows",
+ ((height + tileHeight - 1) / tileHeight) == gridRows);
+ extractor.release();
+ }
+
+ if (checkColor) {
+ Bitmap bitmap = BitmapFactory.decodeFile(filename);
+
+ for (int i = 0; i < COLOR_BARS.length; i++) {
+ Rect r = getColorBarRect(i, width, height);
+ assertTrue("Color bar " + i + " doesn't match", approxEquals(COLOR_BARS[i],
+ Color.valueOf(bitmap.getPixel(r.centerX(), r.centerY()))));
+ }
+
+ Rect r = getColorBlockRect(primary, width, height);
+ assertTrue("Color block doesn't match", approxEquals(COLOR_BLOCK,
+ Color.valueOf(bitmap.getPixel(r.centerX(), height - r.centerY()))));
+
+ bitmap.recycle();
+ }
+ }
+
+ protected void closeQuietly(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ protected int copy(InputStream in, OutputStream out) throws IOException {
+ int total = 0;
+ byte[] buffer = new byte[8192];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+ }
+ return total;
+ }
+
+ protected boolean hasEncoderForMime(String mime) {
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (info.isEncoder()) {
+ for (String type : info.getSupportedTypes()) {
+ if (type.equalsIgnoreCase(mime)) {
+ Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ protected void drawFrame(int width, int height) {
+ mInputEglSurface.makeCurrent();
+ generateSurfaceFrame(mInputIndex, width, height);
+ mInputEglSurface.setPresentationTime(1000 * computePresentationTime(mInputIndex));
+ mInputEglSurface.swapBuffers();
+ mInputIndex++;
+ }
+
+ protected static class TestConfig {
+ final int mInputMode;
+ final boolean mUseGrid;
+ final boolean mUseHandler;
+ final boolean mUseHighBitDepth;
+ final int mMaxNumImages;
+ final int mActualNumImages;
+ final int mWidth;
+ final int mHeight;
+ final int mRotation;
+ final int mQuality;
+ final String mInputPath;
+ final String mOutputPath;
+ final Bitmap[] mBitmaps;
+
+ TestConfig(int inputMode, boolean useGrid, boolean useHandler, boolean useHighBitDepth,
+ int maxNumImages, int actualNumImages, int width, int height, int rotation,
+ int quality, String inputPath, String outputPath, Bitmap[] bitmaps) {
+ mInputMode = inputMode;
+ mUseGrid = useGrid;
+ mUseHandler = useHandler;
+ mUseHighBitDepth = useHighBitDepth;
+ mMaxNumImages = maxNumImages;
+ mActualNumImages = actualNumImages;
+ mWidth = width;
+ mHeight = height;
+ mRotation = rotation;
+ mQuality = quality;
+ mInputPath = inputPath;
+ mOutputPath = outputPath;
+ mBitmaps = bitmaps;
+ }
+
+ static class Builder {
+ final int mInputMode;
+ final boolean mUseGrid;
+ final boolean mUseHandler;
+ boolean mUseHighBitDepth;
+ int mMaxNumImages;
+ int mNumImages;
+ int mWidth;
+ int mHeight;
+ int mRotation;
+ final int mQuality;
+ String mInputPath;
+ final String mOutputPath;
+ Bitmap[] mBitmaps;
+ boolean mNumImagesSetExplicitly;
+
+
+ Builder(int inputMode, boolean useGrids, boolean useHandler, String outputFileName) {
+ mInputMode = inputMode;
+ mUseGrid = useGrids;
+ mUseHandler = useHandler;
+ mUseHighBitDepth = false;
+ mMaxNumImages = mNumImages = 4;
+ mWidth = 1920;
+ mHeight = 1080;
+ mRotation = 0;
+ mQuality = 100;
+ mOutputPath = new File(getApplicationContext().getExternalFilesDir(null),
+ outputFileName).getAbsolutePath();
+ }
+
+ Builder setInputPath(String inputPath) {
+ mInputPath = (mInputMode == INPUT_MODE_BITMAP) ? inputPath : null;
+ return this;
+ }
+
+ Builder setNumImages(int numImages) {
+ mNumImagesSetExplicitly = true;
+ mNumImages = numImages;
+ return this;
+ }
+
+ Builder setRotation(int rotation) {
+ mRotation = rotation;
+ return this;
+ }
+
+ Builder setHighBitDepthEnabled(boolean useHighBitDepth) {
+ mUseHighBitDepth = useHighBitDepth;
+ return this;
+ }
+
+ private void loadBitmapInputs() {
+ if (mInputMode != INPUT_MODE_BITMAP) {
+ return;
+ }
+ if (!mUseHighBitDepth) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(mInputPath);
+ String hasImage = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
+ if (!"yes".equals(hasImage)) {
+ throw new IllegalArgumentException("no bitmap found!");
+ }
+ mMaxNumImages = Math.min(mMaxNumImages,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
+ if (!mNumImagesSetExplicitly) {
+ mNumImages = mMaxNumImages;
+ }
+ mBitmaps = new Bitmap[mMaxNumImages];
+ for (int i = 0; i < mBitmaps.length; i++) {
+ mBitmaps[i] = retriever.getImageAtIndex(i);
+ }
+ mWidth = mBitmaps[0].getWidth();
+ mHeight = mBitmaps[0].getHeight();
+ try {
+ retriever.release();
+ } catch (IOException e) {
+ // Nothing we can do about it.
+ }
+ } else {
+ mMaxNumImages = 1;
+ mNumImages = 1;
+ }
+ }
+
+ private void cleanupStaleOutputs() {
+ File outputFile = new File(mOutputPath);
+ if (outputFile.exists()) {
+ outputFile.delete();
+ }
+ }
+
+ TestConfig build() {
+ cleanupStaleOutputs();
+ loadBitmapInputs();
+
+ return new TestConfig(mInputMode, mUseGrid, mUseHandler, mUseHighBitDepth,
+ mMaxNumImages, mNumImages, mWidth, mHeight, mRotation, mQuality, mInputPath,
+ mOutputPath, mBitmaps);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TestConfig"
+ + ": mInputMode " + mInputMode
+ + ", mUseGrid " + mUseGrid
+ + ", mUseHandler " + mUseHandler
+ + ", mMaxNumImages " + mMaxNumImages
+ + ", mNumImages " + mActualNumImages
+ + ", mWidth " + mWidth
+ + ", mHeight " + mHeight
+ + ", mRotation " + mRotation
+ + ", mQuality " + mQuality
+ + ", mInputPath " + mInputPath
+ + ", mOutputPath " + mOutputPath;
+ }
+ }
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/androidTest/res/raw/heifwriter_input10.png b/heifwriter/heifwriter/src/androidTest/res/raw/heifwriter_input10.png
new file mode 100644
index 0000000..55503df
--- /dev/null
+++ b/heifwriter/heifwriter/src/androidTest/res/raw/heifwriter_input10.png
Binary files differ
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifEncoder.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifEncoder.java
new file mode 100644
index 0000000..561e0b2
--- /dev/null
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifEncoder.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.heifwriter;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.IOException;
+
+/**
+ * This class encodes images into HEIF-compatible samples using AV1 encoder.
+ *
+ * It currently supports three input modes: {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ *
+ * The output format and samples are sent back in {@link
+ * Callback#onOutputFormatChanged(HeifEncoder, MediaFormat)} and {@link
+ * Callback#onDrainOutputBuffer(HeifEncoder, ByteBuffer)}. If the client
+ * requests to use grid, each tile will be sent back individually.
+ *
+ * HeifEncoder is made a separate class from {@link HeifWriter}, as some more
+ * advanced use cases might want to build solutions on top of the HeifEncoder directly.
+ * (eg. mux still images and video tracks into a single container).
+ *
+ * @hide
+ */
+public final class AvifEncoder extends EncoderBase {
+ private static final String TAG = "AvifEncoder";
+ private static final boolean DEBUG = false;
+
+ protected static final int GRID_WIDTH = 512;
+ protected static final int GRID_HEIGHT = 512;
+ protected static final double MAX_COMPRESS_RATIO = 0.25f;
+
+ private static final MediaCodecList sMCL =
+ new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+
+ /**
+ * Configure the avif encoding session. Should only be called once.
+ *
+ * @param width Width of the image.
+ * @param height Height of the image.
+ * @param useGrid Whether to encode image into tiles. If enabled, tile size will be
+ * automatically chosen.
+ * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
+ * supported by this implementation (which often results in larger file size).
+ * @param inputMode The input type of this encoding session.
+ * @param handler If not null, client will receive all callbacks on the handler's looper.
+ * Otherwise, client will receive callbacks on a looper created by us.
+ * @param cb The callback to receive various messages from the avif encoder.
+ */
+ public AvifEncoder(int width, int height, boolean useGrid,
+ int quality, @InputMode int inputMode,
+ @Nullable Handler handler, @NonNull Callback cb,
+ boolean useBitDepth10) throws IOException {
+ super("AVIF", width, height, useGrid, quality, inputMode, handler, cb, useBitDepth10);
+ mEncoder.setCallback(new Av1EncoderCallback(), mHandler);
+ finishSettingUpEncoder(useBitDepth10);
+ }
+
+ protected static String findAv1Fallback() {
+ String av1 = null; // first AV1 encoder
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (!info.isEncoder()) {
+ continue;
+ }
+ MediaCodecInfo.CodecCapabilities caps = null;
+ try {
+ caps = info.getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AV1);
+ } catch (IllegalArgumentException e) { // mime is not supported
+ continue;
+ }
+ if (!caps.getVideoCapabilities().isSizeSupported(GRID_WIDTH, GRID_HEIGHT)) {
+ continue;
+ }
+ if (caps.getEncoderCapabilities().isBitrateModeSupported(
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
+ // Encoder that supports CQ mode is preferred over others,
+ // return the first encoder that supports CQ mode.
+ // (No need to check if it's hw based, it's already listed in
+ // order of preference.)
+ return info.getName();
+ }
+ if (av1 == null) {
+ av1 = info.getName();
+ }
+ }
+ // If no encoders support CQ, return the first AV1 encoder.
+ return av1;
+ }
+
+ /**
+ * MediaCodec callback for AV1 encoding.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected class Av1EncoderCallback extends EncoderCallback {
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ if (codec != mEncoder) return;
+
+ if (DEBUG) Log.d(TAG, "onOutputFormatChanged: " + format);
+
+ // TODO(b/252835975) replace "image/avif" with MIMETYPE_IMAGE_AVIF.
+ if (!format.getString(MediaFormat.KEY_MIME).equals("image/avif")) {
+ format.setString(MediaFormat.KEY_MIME, "image/avif");
+ format.setInteger(MediaFormat.KEY_WIDTH, mWidth);
+ format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
+
+ if (mUseGrid) {
+ format.setInteger(MediaFormat.KEY_TILE_WIDTH, mGridWidth);
+ format.setInteger(MediaFormat.KEY_TILE_HEIGHT, mGridHeight);
+ format.setInteger(MediaFormat.KEY_GRID_ROWS, mGridRows);
+ format.setInteger(MediaFormat.KEY_GRID_COLUMNS, mGridCols);
+ }
+ }
+
+ mCallback.onOutputFormatChanged(AvifEncoder.this, format);
+ }
+ }
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifWriter.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifWriter.java
new file mode 100644
index 0000000..706f9dfd
--- /dev/null
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/AvifWriter.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.heifwriter;
+
+import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class writes one or more still images (of the same dimensions) into
+ * an AVIF file.
+ *
+ * It currently supports three input modes: {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ *
+ * The general sequence (in pseudo-code) to write a avif file using this class is as follows:
+ *
+ * 1) Construct the writer:
+ * AvifWriter avifwriter = new AvifWriter(...);
+ *
+ * 2) If using surface input mode, obtain the input surface:
+ * Surface surface = avifwriter.getInputSurface();
+ *
+ * 3) Call start:
+ * avifwriter.start();
+ *
+ * 4) Depending on the chosen input mode, add one or more images using one of these methods:
+ * avifwriter.addYuvBuffer(...); Or
+ * avifwriter.addBitmap(...); Or
+ * render to the previously obtained surface
+ *
+ * 5) Call stop:
+ * avifwriter.stop(...);
+ *
+ * 6) Close the writer:
+ * avifwriter.close();
+ *
+ * Please refer to the documentations on individual methods for the exact usage.
+ */
+@SuppressWarnings("HiddenSuperclass")
+public final class AvifWriter extends WriterBase {
+
+ private static final String TAG = "AvifWriter";
+ private static final boolean DEBUG = false;
+
+ /**
+ * The input mode where the client adds input buffers with YUV data.
+ *
+ * @see #addYuvBuffer(int, byte[])
+ */
+ public static final int INPUT_MODE_BUFFER = WriterBase.INPUT_MODE_BUFFER;
+
+ /**
+ * The input mode where the client renders the images to an input Surface created by the writer.
+ *
+ * The input surface operates in single buffer mode. As a result, for use case where camera
+ * directly outputs to the input surface, this mode will not work because camera framework
+ * requires multiple buffers to operate in a pipeline fashion.
+ *
+ * @see #getInputSurface()
+ */
+ public static final int INPUT_MODE_SURFACE = WriterBase.INPUT_MODE_SURFACE;
+
+ /**
+ * The input mode where the client adds bitmaps.
+ *
+ * @see #addBitmap(Bitmap)
+ */
+ public static final int INPUT_MODE_BITMAP = WriterBase.INPUT_MODE_BITMAP;
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ INPUT_MODE_BUFFER, INPUT_MODE_SURFACE, INPUT_MODE_BITMAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InputMode {
+
+ }
+
+ /**
+ * Builder class for constructing a AvifWriter object from specified parameters.
+ */
+ public static final class Builder {
+ private final String mPath;
+ private final FileDescriptor mFd;
+ private final int mWidth;
+ private final int mHeight;
+ private final @InputMode int mInputMode;
+ private boolean mGridEnabled = true;
+ private int mQuality = 100;
+ private int mMaxImages = 1;
+ private int mPrimaryIndex = 0;
+ private int mRotation = 0;
+ private Handler mHandler;
+ private boolean mHighBitDepthEnabled = false;
+
+ /**
+ * Construct a Builder with output specified by its path.
+ *
+ * @param path Path of the file to be written.
+ * @param width Width of the image in number of pixels.
+ * @param height Height of the image in number of pixels.
+ * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ */
+ public Builder(@NonNull String path,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
+ this(path, null, width, height, inputMode);
+ }
+
+ /**
+ * Construct a Builder with output specified by its file descriptor.
+ *
+ * @param fd File descriptor of the file to be written.
+ * @param width Width of the image in number of pixels.
+ * @param height Height of the image in number of pixels.
+ * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ */
+ public Builder(@NonNull FileDescriptor fd,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
+ this(null, fd, width, height, inputMode);
+ }
+
+ private Builder(String path, FileDescriptor fd,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
+ mPath = path;
+ mFd = fd;
+ mWidth = width;
+ mHeight = height;
+ mInputMode = inputMode;
+ }
+
+ /**
+ * Set the image rotation in degrees.
+ *
+ * @param rotation Rotation angle in degrees (clockwise) of the image, must be 0, 90,
+ * 180 or 270. Default is 0.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setRotation(@IntRange(from = 0) int rotation) {
+ if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
+ throw new IllegalArgumentException("Invalid rotation angle: " + rotation);
+ }
+ mRotation = rotation;
+ return this;
+ }
+
+ /**
+ * Set whether to enable grid option.
+ *
+ * @param gridEnabled Whether to enable grid option. If enabled, the tile size will be
+ * automatically chosen. Default is to enable.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setGridEnabled(boolean gridEnabled) {
+ mGridEnabled = gridEnabled;
+ return this;
+ }
+
+ /**
+ * Set the quality for encoding images.
+ *
+ * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best
+ * quality supported by this implementation. Default is 100.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setQuality(@IntRange(from = 0, to = 100) int quality) {
+ if (quality < 0 || quality > 100) {
+ throw new IllegalArgumentException("Invalid quality: " + quality);
+ }
+ mQuality = quality;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of images to write.
+ *
+ * @param maxImages Max number of images to write. Frames exceeding this number will not be
+ * written to file. The writing can be stopped earlier before this number
+ * of images are written by {@link #stop(long)}, except for the input mode
+ * of {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be
+ * specified (via {@link #setInputEndOfStreamTimestamp(long)} and reached.
+ * Default is 1.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) {
+ if (maxImages <= 0) {
+ throw new IllegalArgumentException("Invalid maxImage: " + maxImages);
+ }
+ mMaxImages = maxImages;
+ return this;
+ }
+
+ /**
+ * Set the primary image index.
+ *
+ * @param primaryIndex Index of the image that should be marked as primary, must be within
+ * range [0, maxImages - 1] inclusive. Default is 0.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setPrimaryIndex(@IntRange(from = 0) int primaryIndex) {
+ mPrimaryIndex = primaryIndex;
+ return this;
+ }
+
+ /**
+ * Provide a handler for the AvifWriter to use.
+ *
+ * @param handler If not null, client will receive all callbacks on the handler's looper.
+ * Otherwise, client will receive callbacks on a looper created by the
+ * writer. Default is null.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setHandler(@Nullable Handler handler) {
+ mHandler = handler;
+ return this;
+ }
+
+ /**
+ * Provide a setting for the AvifWriter to use high bit-depth or not.
+ *
+ * @param highBitDepthEnabled Whether to enable high bit-depth mode. Default is false, if
+ * true, AvifWriter will encode with high bit-depth.
+ * @return this Builder object.
+ */
+ public @NonNull Builder setHighBitDepthEnabled(boolean highBitDepthEnabled) {
+ mHighBitDepthEnabled = highBitDepthEnabled;
+ return this;
+ }
+
+ /**
+ * Build a AvifWriter object.
+ *
+ * @return a AvifWriter object built according to the specifications.
+ * @throws IOException if failed to create the writer, possibly due to failure to create
+ * {@link android.media.MediaMuxer} or {@link android.media.MediaCodec}.
+ */
+ public @NonNull AvifWriter build() throws IOException {
+ return new AvifWriter(mPath, mFd, mWidth, mHeight, mRotation, mGridEnabled, mQuality,
+ mMaxImages, mPrimaryIndex, mInputMode, mHandler, mHighBitDepthEnabled);
+ }
+ }
+
+ @SuppressLint("WrongConstant")
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ AvifWriter(@NonNull String path,
+ @NonNull FileDescriptor fd,
+ int width,
+ int height,
+ int rotation,
+ boolean gridEnabled,
+ int quality,
+ int maxImages,
+ int primaryIndex,
+ @InputMode int inputMode,
+ @Nullable Handler handler,
+ boolean highBitDepthEnabled) throws IOException {
+ super(rotation, inputMode, maxImages, primaryIndex, gridEnabled, quality,
+ handler, highBitDepthEnabled);
+
+ if (DEBUG) {
+ Log.d(TAG, "width: " + width
+ + ", height: " + height
+ + ", rotation: " + rotation
+ + ", gridEnabled: " + gridEnabled
+ + ", quality: " + quality
+ + ", maxImages: " + maxImages
+ + ", primaryIndex: " + primaryIndex
+ + ", inputMode: " + inputMode);
+ }
+
+ // set to 1 initially, and wait for output format to know for sure
+ mNumTiles = 1;
+
+ mMuxer = (path != null) ? new MediaMuxer(path, MUXER_OUTPUT_HEIF)
+ : new MediaMuxer(fd, MUXER_OUTPUT_HEIF);
+
+ mEncoder = new AvifEncoder(width, height, gridEnabled, quality,
+ mInputMode, mHandler, new WriterCallback(), highBitDepthEnabled);
+ }
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EglWindowSurface.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EglWindowSurface.java
index 9b59d28..4aade72 100644
--- a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EglWindowSurface.java
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EglWindowSurface.java
@@ -25,6 +25,8 @@
import android.util.Log;
import android.view.Surface;
+import androidx.annotation.NonNull;
+
import java.util.Objects;
/**
@@ -50,18 +52,22 @@
* Creates an EglWindowSurface from a Surface.
*/
public EglWindowSurface(Surface surface) {
+ this(surface, false);
+ }
+
+ public EglWindowSurface(Surface surface, boolean useHighBitDepth) {
if (surface == null) {
throw new NullPointerException();
}
mSurface = surface;
- eglSetup();
+ eglSetup(useHighBitDepth);
}
/**
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
*/
- private void eglSetup() {
+ private void eglSetup(boolean useHighBitDepth) {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (Objects.equals(mEGLDisplay, EGL14.EGL_NO_DISPLAY)) {
throw new RuntimeException("unable to get EGL14 display");
@@ -74,27 +80,31 @@
// Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits
// to minimize artifacts from possible YUV conversion.
- int[] attribList = {
- EGL14.EGL_RED_SIZE, 8,
- EGL14.EGL_GREEN_SIZE, 8,
- EGL14.EGL_BLUE_SIZE, 8,
- EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGLExt.EGL_RECORDABLE_ANDROID, 1,
- EGL14.EGL_NONE
+ int eglColorSize = useHighBitDepth ? 10: 8;
+ int eglAlphaSize = useHighBitDepth ? 2: 0;
+ int recordable = useHighBitDepth ? 0: 1;
+ int[] configAttribList = {
+ EGL14.EGL_RED_SIZE, eglColorSize,
+ EGL14.EGL_GREEN_SIZE, eglColorSize,
+ EGL14.EGL_BLUE_SIZE, eglColorSize,
+ EGL14.EGL_ALPHA_SIZE, eglAlphaSize,
+ EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
+ EGLExt.EGL_RECORDABLE_ANDROID, recordable,
+ EGL14.EGL_NONE
};
int[] numConfigs = new int[1];
- if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, mConfigs, 0, mConfigs.length,
- numConfigs, 0)) {
+ if (!EGL14.eglChooseConfig(mEGLDisplay, configAttribList, 0, mConfigs, 0, mConfigs.length,
+ numConfigs, 0)) {
throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
}
// Configure context for OpenGL ES 2.0.
- int[] attrib_list = {
- EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL14.EGL_NONE
+ int[] contextAttribList = {
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mConfigs[0], EGL14.EGL_NO_CONTEXT,
- attrib_list, 0);
+ contextAttribList, 0);
checkEglError("eglCreateContext");
if (mEGLContext == null) {
throw new RuntimeException("null context");
@@ -186,7 +196,7 @@
/**
* Returns the Surface that the MediaCodec receives buffers from.
*/
- public Surface getSurface() {
+ public @NonNull Surface getSurface() {
return mSurface;
}
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EncoderBase.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EncoderBase.java
new file mode 100644
index 0000000..13df5f2
--- /dev/null
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/EncoderBase.java
@@ -0,0 +1,1067 @@
+/*
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.heifwriter;
+
+import android.graphics.Bitmap;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.media.Image;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CodecException;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.opengl.GLES20;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.util.Log;
+import android.util.Range;
+import android.view.Surface;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class holds common utilities for {@link HeifEncoder} and {@link AvifEncoder}, and
+ * calls media framework and encodes images into HEIF- or AVIF- compatible samples using
+ * HEVC or AV1 encoder.
+ *
+ * It currently supports three input modes: {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ *
+ * Callback#onOutputFormatChanged(MediaCodec, MediaFormat)} and {@link
+ * Callback#onDrainOutputBuffer(MediaCodec, ByteBuffer)}. If the client
+ * requests to use grid, each tile will be sent back individually.
+ *
+ *
+ * * HeifEncoder is made a separate class from {@link HeifWriter}, as some more
+ * * advanced use cases might want to build solutions on top of the HeifEncoder directly.
+ * * (eg. mux still images and video tracks into a single container).
+ *
+ *
+ * @hide
+ */
+public class EncoderBase implements AutoCloseable,
+ SurfaceTexture.OnFrameAvailableListener {
+ private static final String TAG = "EncoderBase";
+ private static final boolean DEBUG = false;
+
+ private String MIME;
+ private int GRID_WIDTH;
+ private int GRID_HEIGHT;
+ private double MAX_COMPRESS_RATIO;
+ private int INPUT_BUFFER_POOL_SIZE = 2;
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaCodec mEncoder;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final MediaFormat mCodecFormat;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final Callback mCallback;
+ private final HandlerThread mHandlerThread;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final Handler mHandler;
+ private final @InputMode int mInputMode;
+ private final boolean mUseBitDepth10;
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final int mWidth;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mHeight;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mGridWidth;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mGridHeight;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mGridRows;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mGridCols;
+ private final int mNumTiles;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final boolean mUseGrid;
+
+ private int mInputIndex;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ boolean mInputEOS;
+ private final Rect mSrcRect;
+ private final Rect mDstRect;
+ private ByteBuffer mCurrentBuffer;
+ private final ArrayList<ByteBuffer> mEmptyBuffers = new ArrayList<>();
+ private final ArrayList<ByteBuffer> mFilledBuffers = new ArrayList<>();
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final ArrayList<Integer> mCodecInputBuffers = new ArrayList<>();
+ private final boolean mCopyTiles;
+
+ // Helper for tracking EOS when surface is used
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ SurfaceEOSTracker mEOSTracker;
+
+ // Below variables are to handle GL copy from client's surface
+ // to encoder surface when tiles are used.
+ private SurfaceTexture mInputTexture;
+ private Surface mInputSurface;
+ private Surface mEncoderSurface;
+ private EglWindowSurface mEncoderEglSurface;
+ private EglRectBlt mRectBlt;
+ private int mTextureId;
+ private final float[] mTmpMatrix = new float[16];
+ private final AtomicBoolean mStopping = new AtomicBoolean(false);
+
+ public static final int INPUT_MODE_BUFFER = HeifWriter.INPUT_MODE_BUFFER;
+ public static final int INPUT_MODE_SURFACE = HeifWriter.INPUT_MODE_SURFACE;
+ public static final int INPUT_MODE_BITMAP = HeifWriter.INPUT_MODE_BITMAP;
+ @IntDef({
+ INPUT_MODE_BUFFER,
+ INPUT_MODE_SURFACE,
+ INPUT_MODE_BITMAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InputMode {}
+
+ public static abstract class Callback {
+ /**
+ * Called when the output format has changed.
+ *
+ * @param encoder The EncoderBase object.
+ * @param format The new output format.
+ */
+ public abstract void onOutputFormatChanged(
+ @NonNull EncoderBase encoder, @NonNull MediaFormat format);
+
+ /**
+ * Called when an output buffer becomes available.
+ *
+ * @param encoder The EncoderBase object.
+ * @param byteBuffer the available output buffer.
+ */
+ public abstract void onDrainOutputBuffer(
+ @NonNull EncoderBase encoder, @NonNull ByteBuffer byteBuffer);
+
+ /**
+ * Called when encoding reached the end of stream without error.
+ *
+ * @param encoder The EncoderBase object.
+ */
+ public abstract void onComplete(@NonNull EncoderBase encoder);
+
+ /**
+ * Called when encoding hits an error.
+ *
+ * @param encoder The EncoderBase object.
+ * @param e The exception that the codec reported.
+ */
+ public abstract void onError(@NonNull EncoderBase encoder, @NonNull CodecException e);
+ }
+
+ /**
+ * Configure the encoder. Should only be called once.
+ *
+ * @param mimeType mime type. Currently it supports "HEIC" and "AVIF".
+ * @param width Width of the image.
+ * @param height Height of the image.
+ * @param useGrid Whether to encode image into tiles. If enabled, tile size will be
+ * automatically chosen.
+ * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
+ * supported by this implementation (which often results in larger file size).
+ * @param inputMode The input type of this encoding session.
+ * @param handler If not null, client will receive all callbacks on the handler's looper.
+ * Otherwise, client will receive callbacks on a looper created by us.
+ * @param cb The callback to receive various messages from the heif encoder.
+ */
+ protected EncoderBase(@NonNull String mimeType, int width, int height, boolean useGrid,
+ int quality, @InputMode int inputMode,
+ @Nullable Handler handler, @NonNull Callback cb,
+ boolean useBitDepth10) throws IOException {
+ if (DEBUG)
+ Log.d(TAG, "width: " + width + ", height: " + height +
+ ", useGrid: " + useGrid + ", quality: " + quality +
+ ", inputMode: " + inputMode +
+ ", useBitDepth10: " + String.valueOf(useBitDepth10));
+
+ if (width < 0 || height < 0 || quality < 0 || quality > 100) {
+ throw new IllegalArgumentException("invalid encoder inputs");
+ }
+
+ switch (mimeType) {
+ case "HEIC":
+ MIME = mimeType;
+ GRID_WIDTH = HeifEncoder.GRID_WIDTH;
+ GRID_HEIGHT = HeifEncoder.GRID_HEIGHT;
+ MAX_COMPRESS_RATIO = HeifEncoder.MAX_COMPRESS_RATIO;
+ break;
+ case "AVIF":
+ MIME = mimeType;
+ GRID_WIDTH = AvifEncoder.GRID_WIDTH;
+ GRID_HEIGHT = AvifEncoder.GRID_HEIGHT;
+ MAX_COMPRESS_RATIO = AvifEncoder.MAX_COMPRESS_RATIO;
+ break;
+ default:
+ Log.e(TAG, "Not supported mime type: " + mimeType);
+ }
+
+ boolean useHeicEncoder = false;
+ MediaCodecInfo.CodecCapabilities caps = null;
+ switch (MIME) {
+ case "HEIC":
+ try {
+ mEncoder = MediaCodec.createEncoderByType(
+ MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
+ caps = mEncoder.getCodecInfo().getCapabilitiesForType(
+ MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
+ // If the HEIC encoder can't support the size, fall back to HEVC encoder.
+ if (!caps.getVideoCapabilities().isSizeSupported(width, height)) {
+ mEncoder.release();
+ mEncoder = null;
+ throw new Exception();
+ }
+ useHeicEncoder = true;
+ } catch (Exception e) {
+ mEncoder = MediaCodec.createByCodecName(HeifEncoder.findHevcFallback());
+ caps = mEncoder.getCodecInfo()
+ .getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ // Disable grid if the image is too small
+ useGrid &= (width > GRID_WIDTH || height > GRID_HEIGHT);
+ // Always enable grid if the size is too large for the HEVC encoder
+ useGrid |= !caps.getVideoCapabilities().isSizeSupported(width, height);
+ }
+ break;
+ case "AVIF":
+ mEncoder = MediaCodec.createByCodecName(AvifEncoder.findAv1Fallback());
+ caps = mEncoder.getCodecInfo()
+ .getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AV1);
+ // Disable grid if the image is too small
+ useGrid &= (width > GRID_WIDTH || height > GRID_HEIGHT);
+ // Always enable grid if the size is too large for the AV1 encoder
+ useGrid |= !caps.getVideoCapabilities().isSizeSupported(width, height);
+ break;
+ default:
+ Log.e(TAG, "Not supported mime type: " + MIME);
+ }
+
+ mInputMode = inputMode;
+ mUseBitDepth10 = useBitDepth10;
+ mCallback = cb;
+
+ Looper looper = (handler != null) ? handler.getLooper() : null;
+ if (looper == null) {
+ mHandlerThread = new HandlerThread("HeifEncoderThread",
+ Process.THREAD_PRIORITY_FOREGROUND);
+ mHandlerThread.start();
+ looper = mHandlerThread.getLooper();
+ } else {
+ mHandlerThread = null;
+ }
+ mHandler = new Handler(looper);
+ boolean useSurfaceInternally =
+ (inputMode == INPUT_MODE_SURFACE) || (inputMode == INPUT_MODE_BITMAP);
+ int colorFormat = useSurfaceInternally ? CodecCapabilities.COLOR_FormatSurface :
+ (useBitDepth10 ? CodecCapabilities.COLOR_FormatYUVP010 :
+ CodecCapabilities.COLOR_FormatYUV420Flexible);
+ mCopyTiles = (useGrid && !useHeicEncoder) || (inputMode == INPUT_MODE_BITMAP);
+
+ mWidth = width;
+ mHeight = height;
+ mUseGrid = useGrid;
+
+ int gridWidth, gridHeight, gridRows, gridCols;
+
+ if (useGrid) {
+ gridWidth = GRID_WIDTH;
+ gridHeight = GRID_HEIGHT;
+ gridRows = (height + GRID_HEIGHT - 1) / GRID_HEIGHT;
+ gridCols = (width + GRID_WIDTH - 1) / GRID_WIDTH;
+ } else {
+ gridWidth = mWidth;
+ gridHeight = mHeight;
+ gridRows = 1;
+ gridCols = 1;
+ }
+
+ MediaFormat codecFormat;
+ if (useHeicEncoder) {
+ codecFormat = MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC, mWidth, mHeight);
+ } else {
+ codecFormat = MediaFormat.createVideoFormat(
+ MediaFormat.MIMETYPE_VIDEO_HEVC, gridWidth, gridHeight);
+ }
+
+ if (useGrid) {
+ codecFormat.setInteger(MediaFormat.KEY_TILE_WIDTH, gridWidth);
+ codecFormat.setInteger(MediaFormat.KEY_TILE_HEIGHT, gridHeight);
+ codecFormat.setInteger(MediaFormat.KEY_GRID_COLUMNS, gridCols);
+ codecFormat.setInteger(MediaFormat.KEY_GRID_ROWS, gridRows);
+ }
+
+ if (useHeicEncoder) {
+ mGridWidth = width;
+ mGridHeight = height;
+ mGridRows = 1;
+ mGridCols = 1;
+ } else {
+ mGridWidth = gridWidth;
+ mGridHeight = gridHeight;
+ mGridRows = gridRows;
+ mGridCols = gridCols;
+ }
+ mNumTiles = mGridRows * mGridCols;
+
+ codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
+ codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+ codecFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mNumTiles);
+
+ // When we're doing tiles, set the operating rate higher as the size
+ // is small, otherwise set to the normal 30fps.
+ if (mNumTiles > 1) {
+ codecFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, 120);
+ } else {
+ codecFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, 30);
+ }
+
+ if (useSurfaceInternally && !mCopyTiles) {
+ // Use fixed PTS gap and disable backward frame drop
+ Log.d(TAG, "Setting fixed pts gap");
+ codecFormat.setLong(MediaFormat.KEY_MAX_PTS_GAP_TO_ENCODER, -1000000);
+ }
+
+ MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
+
+ if (encoderCaps.isBitrateModeSupported(
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
+ Log.d(TAG, "Setting bitrate mode to constant quality");
+ Range<Integer> qualityRange = encoderCaps.getQualityRange();
+ Log.d(TAG, "Quality range: " + qualityRange);
+ codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ);
+ codecFormat.setInteger(MediaFormat.KEY_QUALITY, (int) (qualityRange.getLower() +
+ (qualityRange.getUpper() - qualityRange.getLower()) * quality / 100.0));
+ } else {
+ if (encoderCaps.isBitrateModeSupported(
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) {
+ Log.d(TAG, "Setting bitrate mode to constant bitrate");
+ codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
+ } else { // assume VBR
+ Log.d(TAG, "Setting bitrate mode to variable bitrate");
+ codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
+ }
+ // Calculate the bitrate based on image dimension, max compression ratio and quality.
+ // Note that we set the frame rate to the number of tiles, so the bitrate would be the
+ // intended bits for one image.
+ int bitrate = caps.getVideoCapabilities().getBitrateRange().clamp(
+ (int) (width * height * 1.5 * 8 * MAX_COMPRESS_RATIO * quality / 100.0f));
+ codecFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+ }
+
+ mCodecFormat = codecFormat;
+
+ mDstRect = new Rect(0, 0, mGridWidth, mGridHeight);
+ mSrcRect = new Rect();
+ }
+
+ /**
+ * Finish setting up the encoder.
+ * Call MediaCodec.configure() method so that mEncoder enters configured stage, then add input
+ * surface or add input buffers if needed.
+ *
+ * Note: this method must be called after the constructor.
+ */
+ protected void finishSettingUpEncoder(boolean useBitDepth10) {
+ boolean useSurfaceInternally =
+ (mInputMode == INPUT_MODE_SURFACE) || (mInputMode == INPUT_MODE_BITMAP);
+
+ mEncoder.configure(mCodecFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+
+ if (useSurfaceInternally) {
+ mEncoderSurface = mEncoder.createInputSurface();
+
+ mEOSTracker = new SurfaceEOSTracker(mCopyTiles);
+
+ if (mCopyTiles) {
+ mEncoderEglSurface = new EglWindowSurface(mEncoderSurface, useBitDepth10);
+ mEncoderEglSurface.makeCurrent();
+
+ mRectBlt = new EglRectBlt(
+ new Texture2dProgram((mInputMode == INPUT_MODE_BITMAP)
+ ? Texture2dProgram.TEXTURE_2D
+ : Texture2dProgram.TEXTURE_EXT),
+ mWidth, mHeight);
+
+ mTextureId = mRectBlt.createTextureObject();
+
+ if (mInputMode == INPUT_MODE_SURFACE) {
+ // use single buffer mode to block on input
+ mInputTexture = new SurfaceTexture(mTextureId, true);
+ mInputTexture.setOnFrameAvailableListener(this);
+ mInputTexture.setDefaultBufferSize(mWidth, mHeight);
+ mInputSurface = new Surface(mInputTexture);
+ }
+
+ // make uncurrent since onFrameAvailable could be called on arbituray thread.
+ // making the context current on a different thread will cause error.
+ mEncoderEglSurface.makeUnCurrent();
+ } else {
+ mInputSurface = mEncoderSurface;
+ }
+ } else {
+ for (int i = 0; i < INPUT_BUFFER_POOL_SIZE; i++) {
+ int bufferSize = mUseBitDepth10 ? mWidth * mHeight * 3 : mWidth * mHeight * 3 / 2;
+ mEmptyBuffers.add(ByteBuffer.allocateDirect(bufferSize));
+ }
+ }
+ }
+
+ /**
+ * Copies from source frame to encoder inputs using GL. The source could be either
+ * client's input surface, or the input bitmap loaded to texture.
+ */
+ private void copyTilesGL() {
+ GLES20.glViewport(0, 0, mGridWidth, mGridHeight);
+
+ for (int row = 0; row < mGridRows; row++) {
+ for (int col = 0; col < mGridCols; col++) {
+ int left = col * mGridWidth;
+ int top = row * mGridHeight;
+ mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
+ try {
+ mRectBlt.copyRect(mTextureId, Texture2dProgram.V_FLIP_MATRIX, mSrcRect);
+ } catch (RuntimeException e) {
+ // EGL copy could throw if the encoder input surface is no longer valid
+ // after encoder is released. This is not an error because we're already
+ // stopping (either after EOS is received or requested by client).
+ if (mStopping.get()) {
+ return;
+ }
+ throw e;
+ }
+ mEncoderEglSurface.setPresentationTime(
+ 1000 * computePresentationTime(mInputIndex++));
+ mEncoderEglSurface.swapBuffers();
+ }
+ }
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ synchronized (this) {
+ if (mEncoderEglSurface == null) {
+ return;
+ }
+
+ mEncoderEglSurface.makeCurrent();
+
+ surfaceTexture.updateTexImage();
+ surfaceTexture.getTransformMatrix(mTmpMatrix);
+
+ long timestampNs = surfaceTexture.getTimestamp();
+
+ if (DEBUG) Log.d(TAG, "onFrameAvailable: timestampUs " + (timestampNs / 1000));
+
+ boolean takeFrame = mEOSTracker.updateLastInputAndEncoderTime(timestampNs,
+ computePresentationTime(mInputIndex + mNumTiles - 1));
+
+ if (takeFrame) {
+ copyTilesGL();
+ }
+
+ surfaceTexture.releaseTexImage();
+
+ // make uncurrent since the onFrameAvailable could be called on arbituray thread.
+ // making the context current on a different thread will cause error.
+ mEncoderEglSurface.makeUnCurrent();
+ }
+ }
+
+ /**
+ * Start the encoding process.
+ */
+ public void start() {
+ mEncoder.start();
+ }
+
+ /**
+ * Add one YUV buffer to be encoded. This might block if the encoder can't process the input
+ * buffers fast enough.
+ *
+ * After the call returns, the client can reuse the data array.
+ *
+ * @param format The YUV format as defined in {@link android.graphics.ImageFormat}, currently
+ * only support YUV_420_888.
+ *
+ * @param data byte array containing the YUV data. If the format has more than one planes,
+ * they must be concatenated.
+ */
+ public void addYuvBuffer(int format, @NonNull byte[] data) {
+ if (mInputMode != INPUT_MODE_BUFFER) {
+ throw new IllegalStateException(
+ "addYuvBuffer is only allowed in buffer input mode");
+ }
+ if ((mUseBitDepth10 && format != ImageFormat.YCBCR_P010)
+ || (!mUseBitDepth10 && format != ImageFormat.YUV_420_888)) {
+ throw new IllegalStateException("Wrong color format.");
+ }
+ if (data == null
+ || (mUseBitDepth10 && data.length != mWidth * mHeight * 3)
+ || (!mUseBitDepth10 && data.length != mWidth * mHeight * 3 / 2)) {
+ throw new IllegalArgumentException("invalid data");
+ }
+ addYuvBufferInternal(data);
+ }
+
+ /**
+ * Retrieves the input surface for encoding.
+ *
+ * Will only return valid value if configured to use surface input.
+ */
+ public @NonNull Surface getInputSurface() {
+ if (mInputMode != INPUT_MODE_SURFACE) {
+ throw new IllegalStateException(
+ "getInputSurface is only allowed in surface input mode");
+ }
+ return mInputSurface;
+ }
+
+ /**
+ * Sets the timestamp (in nano seconds) of the last input frame to encode. Frames with
+ * timestamps larger than the specified value will not be encoded. However, if a frame
+ * already started encoding when this is set, all tiles within that frame will be encoded.
+ *
+ * This method only applies when surface is used.
+ */
+ public void setEndOfInputStreamTimestamp(long timestampNs) {
+ if (mInputMode != INPUT_MODE_SURFACE) {
+ throw new IllegalStateException(
+ "setEndOfInputStreamTimestamp is only allowed in surface input mode");
+ }
+ if (mEOSTracker != null) {
+ mEOSTracker.updateInputEOSTime(timestampNs);
+ }
+ }
+
+ /**
+ * Adds one bitmap to be encoded.
+ */
+ public void addBitmap(@NonNull Bitmap bitmap) {
+ if (mInputMode != INPUT_MODE_BITMAP) {
+ throw new IllegalStateException("addBitmap is only allowed in bitmap input mode");
+ }
+
+ boolean takeFrame = mEOSTracker.updateLastInputAndEncoderTime(
+ computePresentationTime(mInputIndex) * 1000,
+ computePresentationTime(mInputIndex + mNumTiles - 1));
+
+ if (!takeFrame) return;
+
+ synchronized (this) {
+ if (mEncoderEglSurface == null) {
+ return;
+ }
+
+ mEncoderEglSurface.makeCurrent();
+
+ mRectBlt.loadTexture(mTextureId, bitmap);
+
+ copyTilesGL();
+
+ // make uncurrent since the onFrameAvailable could be called on arbituray thread.
+ // making the context current on a different thread will cause error.
+ mEncoderEglSurface.makeUnCurrent();
+ }
+ }
+
+ /**
+ * Sends input EOS to the encoder. Result will be notified asynchronously via
+ * {@link Callback#onComplete(EncoderBase)} if encoder reaches EOS without error, or
+ * {@link Callback#onError(EncoderBase, CodecException)} otherwise.
+ */
+ public void stopAsync() {
+ if (mInputMode == INPUT_MODE_BITMAP) {
+ // here we simply set the EOS timestamp to 0, so that the cut off will be the last
+ // bitmap ever added.
+ mEOSTracker.updateInputEOSTime(0);
+ } else if (mInputMode == INPUT_MODE_BUFFER) {
+ addYuvBufferInternal(null);
+ }
+ }
+
+ /**
+ * Generates the presentation time for input frame N, in microseconds.
+ * The timestamp advances 1 sec for every whole frame.
+ */
+ private long computePresentationTime(int frameIndex) {
+ return 132 + (long)frameIndex * 1000000 / mNumTiles;
+ }
+
+ /**
+ * Obtains one empty input buffer and copies the data into it. Before input
+ * EOS is sent, this would block until the data is copied. After input EOS
+ * is sent, this would return immediately.
+ */
+ private void addYuvBufferInternal(@Nullable byte[] data) {
+ ByteBuffer buffer = acquireEmptyBuffer();
+ if (buffer == null) {
+ return;
+ }
+ buffer.clear();
+ if (data != null) {
+ buffer.put(data);
+ }
+ buffer.flip();
+ synchronized (mFilledBuffers) {
+ mFilledBuffers.add(buffer);
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ maybeCopyOneTileYUV();
+ }
+ });
+ }
+
+ /**
+ * Routine to copy one tile if we have both input and codec buffer available.
+ *
+ * Must be called on the handler looper that also handles the MediaCodec callback.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ void maybeCopyOneTileYUV() {
+ ByteBuffer currentBuffer;
+ while ((currentBuffer = getCurrentBuffer()) != null && !mCodecInputBuffers.isEmpty()) {
+ int index = mCodecInputBuffers.remove(0);
+
+ // 0-length input means EOS.
+ boolean inputEOS = (mInputIndex % mNumTiles == 0) && (currentBuffer.remaining() == 0);
+
+ if (!inputEOS) {
+ Image image = mEncoder.getInputImage(index);
+ int left = mGridWidth * (mInputIndex % mGridCols);
+ int top = mGridHeight * (mInputIndex / mGridCols % mGridRows);
+ mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
+ copyOneTileYUV(currentBuffer, image, mWidth, mHeight, mSrcRect, mDstRect,
+ mUseBitDepth10);
+ }
+
+ mEncoder.queueInputBuffer(index, 0,
+ inputEOS ? 0 : mEncoder.getInputBuffer(index).capacity(),
+ computePresentationTime(mInputIndex++),
+ inputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+ if (inputEOS || mInputIndex % mNumTiles == 0) {
+ returnEmptyBufferAndNotify(inputEOS);
+ }
+ }
+ }
+
+ /**
+ * Copies from a rect from src buffer to dst image.
+ * TOOD: This will be replaced by JNI.
+ */
+ private static void copyOneTileYUV(ByteBuffer srcBuffer, Image dstImage,
+ int srcWidth, int srcHeight, Rect srcRect, Rect dstRect, boolean useBitDepth10) {
+ if (srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height()) {
+ throw new IllegalArgumentException("src and dst rect size are different!");
+ }
+ if (srcWidth % 2 != 0 || srcHeight % 2 != 0 ||
+ srcRect.left % 2 != 0 || srcRect.top % 2 != 0 ||
+ srcRect.right % 2 != 0 || srcRect.bottom % 2 != 0 ||
+ dstRect.left % 2 != 0 || dstRect.top % 2 != 0 ||
+ dstRect.right % 2 != 0 || dstRect.bottom % 2 != 0) {
+ throw new IllegalArgumentException("src or dst are not aligned!");
+ }
+
+ Image.Plane[] planes = dstImage.getPlanes();
+ if (useBitDepth10) {
+ // Assume pixel format is P010
+ // Y plane, UV interlaced
+ // pixel step = 2
+ for (int n = 0; n < planes.length; n++) {
+ ByteBuffer dstBuffer = planes[n].getBuffer();
+ int colStride = planes[n].getPixelStride();
+ int copyWidth = Math.min(srcRect.width(), srcWidth - srcRect.left);
+ int copyHeight = Math.min(srcRect.height(), srcHeight - srcRect.top);
+ int srcPlanePos = 0, div = 1;
+ if (n > 0) {
+ div = 2;
+ srcPlanePos = srcWidth * srcHeight;
+ if (n == 2) {
+ srcPlanePos += colStride / 2;
+ }
+ }
+ for (int i = 0; i < copyHeight / div; i++) {
+ srcBuffer.position(srcPlanePos +
+ (i + srcRect.top / div) * srcWidth + srcRect.left / div);
+ dstBuffer.position((i + dstRect.top / div) * planes[n].getRowStride()
+ + dstRect.left * colStride / div);
+
+ for (int j = 0; j < copyWidth / div; j++) {
+ dstBuffer.put(srcBuffer.get());
+ dstBuffer.put(srcBuffer.get());
+ if (colStride > 2 /*pixel step*/ && j != copyWidth / div - 1) {
+ dstBuffer.position(dstBuffer.position() + colStride / 2);
+ }
+ }
+ }
+ }
+ } else {
+ // Assume pixel format is YUV_420_Planer
+ // pixel step = 1
+ for (int n = 0; n < planes.length; n++) {
+ ByteBuffer dstBuffer = planes[n].getBuffer();
+ int colStride = planes[n].getPixelStride();
+ int copyWidth = Math.min(srcRect.width(), srcWidth - srcRect.left);
+ int copyHeight = Math.min(srcRect.height(), srcHeight - srcRect.top);
+ int srcPlanePos = 0, div = 1;
+ if (n > 0) {
+ div = 2;
+ srcPlanePos = srcWidth * srcHeight * (n + 3) / 4;
+ }
+ for (int i = 0; i < copyHeight / div; i++) {
+ srcBuffer.position(srcPlanePos +
+ (i + srcRect.top / div) * srcWidth / div + srcRect.left / div);
+ dstBuffer.position((i + dstRect.top / div) * planes[n].getRowStride()
+ + dstRect.left * colStride / div);
+
+ for (int j = 0; j < copyWidth / div; j++) {
+ dstBuffer.put(srcBuffer.get());
+ if (colStride > 1 && j != copyWidth / div - 1) {
+ dstBuffer.position(dstBuffer.position() + colStride - 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private ByteBuffer acquireEmptyBuffer() {
+ synchronized (mEmptyBuffers) {
+ // wait for an empty input buffer first
+ while (!mInputEOS && mEmptyBuffers.isEmpty()) {
+ try {
+ mEmptyBuffers.wait();
+ } catch (InterruptedException e) {}
+ }
+
+ // if already EOS, return null to stop further encoding.
+ return mInputEOS ? null : mEmptyBuffers.remove(0);
+ }
+ }
+
+ /**
+ * Routine to get the current input buffer to copy from.
+ * Only called on callback handler thread.
+ */
+ private ByteBuffer getCurrentBuffer() {
+ if (!mInputEOS && mCurrentBuffer == null) {
+ synchronized (mFilledBuffers) {
+ mCurrentBuffer = mFilledBuffers.isEmpty() ?
+ null : mFilledBuffers.remove(0);
+ }
+ }
+ return mInputEOS ? null : mCurrentBuffer;
+ }
+
+ /**
+ * Routine to put the consumed input buffer back into the empty buffer pool.
+ * Only called on callback handler thread.
+ */
+ private void returnEmptyBufferAndNotify(boolean inputEOS) {
+ synchronized (mEmptyBuffers) {
+ mInputEOS |= inputEOS;
+ mEmptyBuffers.add(mCurrentBuffer);
+ mEmptyBuffers.notifyAll();
+ }
+ mCurrentBuffer = null;
+ }
+
+ /**
+ * Routine to release all resources. Must be run on the same looper that
+ * handles the MediaCodec callbacks.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ void stopInternal() {
+ if (DEBUG) Log.d(TAG, "stopInternal");
+
+ // set stopping, so that the tile copy would bail out
+ // if it hits failure after this point.
+ mStopping.set(true);
+
+ // after start, mEncoder is only accessed on handler, so no need to sync.
+ try {
+ if (mEncoder != null) {
+ mEncoder.stop();
+ mEncoder.release();
+ }
+ } catch (Exception e) {
+ } finally {
+ mEncoder = null;
+ }
+
+ // unblock the addBuffer() if we're tearing down before EOS is sent.
+ synchronized (mEmptyBuffers) {
+ mInputEOS = true;
+ mEmptyBuffers.notifyAll();
+ }
+
+ // Clean up surface and Egl related refs. This lock must come after encoder
+ // release. When we're closing, we insert stopInternal() at the front of queue
+ // so that the shutdown can be processed promptly, this means there might be
+ // some output available requests queued after this. As the tile copies trying
+ // to finish the current frame, there is a chance is might get stuck because
+ // those outputs were not returned. Shutting down the encoder will make break
+ // the tile copier out of that.
+ synchronized(this) {
+ try {
+ if (mRectBlt != null) {
+ mRectBlt.release(false);
+ }
+ } catch (Exception e) {
+ } finally {
+ mRectBlt = null;
+ }
+
+ try {
+ if (mEncoderEglSurface != null) {
+ // Note that this frees mEncoderSurface too. If mEncoderEglSurface is not
+ // there, client is responsible to release the input surface it got from us,
+ // we don't release mEncoderSurface here.
+ mEncoderEglSurface.release();
+ }
+ } catch (Exception e) {
+ } finally {
+ mEncoderEglSurface = null;
+ }
+
+ try {
+ if (mInputTexture != null) {
+ mInputTexture.release();
+ }
+ } catch (Exception e) {
+ } finally {
+ mInputTexture = null;
+ }
+ }
+ }
+
+ /**
+ * This class handles EOS for surface or bitmap inputs.
+ *
+ * When encoding from surface or bitmap, we can't call
+ * {@link MediaCodec#signalEndOfInputStream()} immediately after input is drawn, since this
+ * could drop all pending frames in the buffer queue. When there are tiles, this could leave
+ * us a partially encoded image.
+ *
+ * So here we track the EOS status by timestamps, and only signal EOS to the encoder
+ * when we collected all images we need.
+ *
+ * Since this is updated from multiple threads ({@link #setEndOfInputStreamTimestamp(long)},
+ * {@link EncoderCallback#onOutputBufferAvailable(MediaCodec, int, BufferInfo)},
+ * {@link #addBitmap(Bitmap)} and {@link #onFrameAvailable(SurfaceTexture)}), it must be fully
+ * synchronized.
+ *
+ * Note that when buffer input is used, the EOS flag is set in
+ * {@link EncoderCallback#onInputBufferAvailable(MediaCodec, int)} and this class is not used.
+ */
+ private class SurfaceEOSTracker {
+ private static final boolean DEBUG_EOS = false;
+
+ final boolean mCopyTiles;
+ long mInputEOSTimeNs = -1;
+ long mLastInputTimeNs = -1;
+ long mEncoderEOSTimeUs = -1;
+ long mLastEncoderTimeUs = -1;
+ long mLastOutputTimeUs = -1;
+ boolean mSignaled;
+
+ SurfaceEOSTracker(boolean copyTiles) {
+ mCopyTiles = copyTiles;
+ }
+
+ synchronized void updateInputEOSTime(long timestampNs) {
+ if (DEBUG_EOS) Log.d(TAG, "updateInputEOSTime: " + timestampNs);
+
+ if (mCopyTiles) {
+ if (mInputEOSTimeNs < 0) {
+ mInputEOSTimeNs = timestampNs;
+ }
+ } else {
+ if (mEncoderEOSTimeUs < 0) {
+ mEncoderEOSTimeUs = timestampNs / 1000;
+ }
+ }
+ updateEOSLocked();
+ }
+
+ synchronized boolean updateLastInputAndEncoderTime(long inputTimeNs, long encoderTimeUs) {
+ if (DEBUG_EOS) Log.d(TAG,
+ "updateLastInputAndEncoderTime: " + inputTimeNs + ", " + encoderTimeUs);
+
+ boolean shouldTakeFrame = mInputEOSTimeNs < 0 || inputTimeNs <= mInputEOSTimeNs;
+ if (shouldTakeFrame) {
+ mLastEncoderTimeUs = encoderTimeUs;
+ }
+ mLastInputTimeNs = inputTimeNs;
+ updateEOSLocked();
+ return shouldTakeFrame;
+ }
+
+ synchronized void updateLastOutputTime(long outputTimeUs) {
+ if (DEBUG_EOS) Log.d(TAG, "updateLastOutputTime: " + outputTimeUs);
+
+ mLastOutputTimeUs = outputTimeUs;
+ updateEOSLocked();
+ }
+
+ private void updateEOSLocked() {
+ if (mSignaled) {
+ return;
+ }
+ if (mEncoderEOSTimeUs < 0) {
+ if (mInputEOSTimeNs >= 0 && mLastInputTimeNs >= mInputEOSTimeNs) {
+ if (mLastEncoderTimeUs < 0) {
+ doSignalEOSLocked();
+ return;
+ }
+ // mEncoderEOSTimeUs tracks the timestamp of the last output buffer we
+ // will wait for. When that buffer arrives, encoder will be signalled EOS.
+ mEncoderEOSTimeUs = mLastEncoderTimeUs;
+ if (DEBUG_EOS) Log.d(TAG,
+ "updateEOSLocked: mEncoderEOSTimeUs " + mEncoderEOSTimeUs);
+ }
+ }
+ if (mEncoderEOSTimeUs >= 0 && mEncoderEOSTimeUs <= mLastOutputTimeUs) {
+ doSignalEOSLocked();
+ }
+ }
+
+ private void doSignalEOSLocked() {
+ if (DEBUG_EOS) Log.d(TAG, "doSignalEOSLocked");
+
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ if (mEncoder != null) {
+ mEncoder.signalEndOfInputStream();
+ }
+ }
+ });
+
+ mSignaled = true;
+ }
+ }
+
+
+ /**
+ * MediaCodec callback for HEVC/AV1 encoding.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ abstract class EncoderCallback extends MediaCodec.Callback {
+ private boolean mOutputEOS;
+
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ if (codec != mEncoder || mInputEOS) return;
+
+ if (DEBUG) Log.d(TAG, "onInputBufferAvailable: " + index);
+ mCodecInputBuffers.add(index);
+ maybeCopyOneTileYUV();
+ }
+
+ @Override
+ public void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info) {
+ if (codec != mEncoder || mOutputEOS) return;
+
+ if (DEBUG) {
+ Log.d(TAG, "onOutputBufferAvailable: " + index
+ + ", time " + info.presentationTimeUs
+ + ", size " + info.size
+ + ", flags " + info.flags);
+ }
+
+ if ((info.size > 0) && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0)) {
+ ByteBuffer outputBuffer = codec.getOutputBuffer(index);
+
+ // reset position as addBuffer() modifies it
+ outputBuffer.position(info.offset);
+ outputBuffer.limit(info.offset + info.size);
+
+ if (mEOSTracker != null) {
+ mEOSTracker.updateLastOutputTime(info.presentationTimeUs);
+ }
+
+ mCallback.onDrainOutputBuffer(EncoderBase.this, outputBuffer);
+ }
+
+ mOutputEOS |= ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
+
+ codec.releaseOutputBuffer(index, false);
+
+ if (mOutputEOS) {
+ stopAndNotify(null);
+ }
+ }
+
+ @Override
+ public void onError(MediaCodec codec, CodecException e) {
+ if (codec != mEncoder) return;
+
+ Log.e(TAG, "onError: " + e);
+ stopAndNotify(e);
+ }
+
+ private void stopAndNotify(@Nullable CodecException e) {
+ stopInternal();
+ if (e == null) {
+ mCallback.onComplete(EncoderBase.this);
+ } else {
+ mCallback.onError(EncoderBase.this, e);
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ // unblock the addBuffer() if we're tearing down before EOS is sent.
+ synchronized (mEmptyBuffers) {
+ mInputEOS = true;
+ mEmptyBuffers.notifyAll();
+ }
+
+ mHandler.postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ stopInternal();
+ } catch (Exception e) {
+ // We don't want to crash when closing.
+ }
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
index e520056..a0b0e9a 100644
--- a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
@@ -16,36 +16,20 @@
package androidx.heifwriter;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.media.Image;
import android.media.MediaCodec;
-import android.media.MediaCodec.BufferInfo;
-import android.media.MediaCodec.CodecException;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecList;
import android.media.MediaFormat;
-import android.opengl.GLES20;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
import android.util.Log;
import android.util.Range;
-import android.view.Surface;
-import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class encodes images into HEIF-compatible samples using HEVC encoder.
@@ -62,115 +46,16 @@
* advanced use cases might want to build solutions on top of the HeifEncoder directly.
* (eg. mux still images and video tracks into a single container).
*/
-final class HeifEncoder implements AutoCloseable,
- SurfaceTexture.OnFrameAvailableListener {
+final class HeifEncoder extends EncoderBase {
private static final String TAG = "HeifEncoder";
private static final boolean DEBUG = false;
- private static final int GRID_WIDTH = 512;
- private static final int GRID_HEIGHT = 512;
- private static final double MAX_COMPRESS_RATIO = 0.25f;
- private static final int INPUT_BUFFER_POOL_SIZE = 2;
+ protected static final int GRID_WIDTH = 512;
+ protected static final int GRID_HEIGHT = 512;
+ protected static final double MAX_COMPRESS_RATIO = 0.25f;
private static final MediaCodecList sMCL =
- new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- MediaCodec mEncoder;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final Callback mCallback;
- private final HandlerThread mHandlerThread;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final Handler mHandler;
- private final @InputMode int mInputMode;
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mWidth;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mHeight;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mGridWidth;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mGridHeight;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mGridRows;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mGridCols;
- private final int mNumTiles;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final boolean mUseGrid;
-
- private int mInputIndex;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- boolean mInputEOS;
- private final Rect mSrcRect;
- private final Rect mDstRect;
- private ByteBuffer mCurrentBuffer;
- private final ArrayList<ByteBuffer> mEmptyBuffers = new ArrayList<>();
- private final ArrayList<ByteBuffer> mFilledBuffers = new ArrayList<>();
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final ArrayList<Integer> mCodecInputBuffers = new ArrayList<>();
-
- // Helper for tracking EOS when surface is used
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- SurfaceEOSTracker mEOSTracker;
-
- // Below variables are to handle GL copy from client's surface
- // to encoder surface when tiles are used.
- private SurfaceTexture mInputTexture;
- private Surface mInputSurface;
- private Surface mEncoderSurface;
- private EglWindowSurface mEncoderEglSurface;
- private EglRectBlt mRectBlt;
- private int mTextureId;
- private final float[] mTmpMatrix = new float[16];
- private final AtomicBoolean mStopping = new AtomicBoolean(false);
-
- public static final int INPUT_MODE_BUFFER = HeifWriter.INPUT_MODE_BUFFER;
- public static final int INPUT_MODE_SURFACE = HeifWriter.INPUT_MODE_SURFACE;
- public static final int INPUT_MODE_BITMAP = HeifWriter.INPUT_MODE_BITMAP;
- @IntDef({
- INPUT_MODE_BUFFER,
- INPUT_MODE_SURFACE,
- INPUT_MODE_BITMAP,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface InputMode {}
-
- public static abstract class Callback {
- /**
- * Called when the output format has changed.
- *
- * @param encoder The HeifEncoder object.
- * @param format The new output format.
- */
- public abstract void onOutputFormatChanged(
- @NonNull HeifEncoder encoder, @NonNull MediaFormat format);
-
- /**
- * Called when an output buffer becomes available.
- *
- * @param encoder The HeifEncoder object.
- * @param byteBuffer the available output buffer.
- */
- public abstract void onDrainOutputBuffer(
- @NonNull HeifEncoder encoder, @NonNull ByteBuffer byteBuffer);
-
- /**
- * Called when encoding reached the end of stream without error.
- *
- * @param encoder The HeifEncoder object.
- */
- public abstract void onComplete(@NonNull HeifEncoder encoder);
-
- /**
- * Called when encoding hits an error.
- *
- * @param encoder The HeifEncoder object.
- * @param e The exception that the codec reported.
- */
- public abstract void onError(@NonNull HeifEncoder encoder, @NonNull CodecException e);
- }
+ new MediaCodecList(MediaCodecList.REGULAR_CODECS);
/**
* Configure the heif encoding session. Should only be called once.
@@ -187,198 +72,15 @@
* @param cb The callback to receive various messages from the heif encoder.
*/
public HeifEncoder(int width, int height, boolean useGrid,
- int quality, @InputMode int inputMode,
- @Nullable Handler handler, @NonNull Callback cb) throws IOException {
- if (DEBUG) Log.d(TAG, "width: " + width + ", height: " + height +
- ", useGrid: " + useGrid + ", quality: " + quality + ", inputMode: " + inputMode);
-
- if (width < 0 || height < 0 || quality < 0 || quality > 100) {
- throw new IllegalArgumentException("invalid encoder inputs");
- }
-
- // Disable grid if the image is too small
- useGrid &= (width > GRID_WIDTH || height > GRID_HEIGHT);
-
- boolean useHeicEncoder = false;
- MediaCodecInfo.CodecCapabilities caps = null;
- try {
- mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
- caps = mEncoder.getCodecInfo().getCapabilitiesForType(
- MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
- // If the HEIC encoder can't support the size, fall back to HEVC encoder.
- if (!caps.getVideoCapabilities().isSizeSupported(width, height)) {
- mEncoder.release();
- mEncoder = null;
- throw new Exception();
- }
- useHeicEncoder = true;
- } catch (Exception e) {
- mEncoder = MediaCodec.createByCodecName(findHevcFallback());
- caps = mEncoder.getCodecInfo().getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_HEVC);
- // Always enable grid if the size is too large for the HEVC encoder
- useGrid |= !caps.getVideoCapabilities().isSizeSupported(width, height);
- }
-
- mInputMode = inputMode;
-
- mCallback = cb;
-
- Looper looper = (handler != null) ? handler.getLooper() : null;
- if (looper == null) {
- mHandlerThread = new HandlerThread("HeifEncoderThread",
- Process.THREAD_PRIORITY_FOREGROUND);
- mHandlerThread.start();
- looper = mHandlerThread.getLooper();
- } else {
- mHandlerThread = null;
- }
- mHandler = new Handler(looper);
- boolean useSurfaceInternally =
- (inputMode == INPUT_MODE_SURFACE) || (inputMode == INPUT_MODE_BITMAP);
- int colorFormat = useSurfaceInternally ? CodecCapabilities.COLOR_FormatSurface :
- CodecCapabilities.COLOR_FormatYUV420Flexible;
- boolean copyTiles = (useGrid && !useHeicEncoder) || (inputMode == INPUT_MODE_BITMAP);
-
- mWidth = width;
- mHeight = height;
- mUseGrid = useGrid;
-
- int gridWidth, gridHeight, gridRows, gridCols;
-
- if (useGrid) {
- gridWidth = GRID_WIDTH;
- gridHeight = GRID_HEIGHT;
- gridRows = (height + GRID_HEIGHT - 1) / GRID_HEIGHT;
- gridCols = (width + GRID_WIDTH - 1) / GRID_WIDTH;
- } else {
- gridWidth = mWidth;
- gridHeight = mHeight;
- gridRows = 1;
- gridCols = 1;
- }
-
- MediaFormat codecFormat;
- if (useHeicEncoder) {
- codecFormat = MediaFormat.createVideoFormat(
- MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC, mWidth, mHeight);
- } else {
- codecFormat = MediaFormat.createVideoFormat(
- MediaFormat.MIMETYPE_VIDEO_HEVC, gridWidth, gridHeight);
- }
-
- if (useGrid) {
- codecFormat.setInteger(MediaFormat.KEY_TILE_WIDTH, gridWidth);
- codecFormat.setInteger(MediaFormat.KEY_TILE_HEIGHT, gridHeight);
- codecFormat.setInteger(MediaFormat.KEY_GRID_COLUMNS, gridCols);
- codecFormat.setInteger(MediaFormat.KEY_GRID_ROWS, gridRows);
- }
-
- if (useHeicEncoder) {
- mGridWidth = width;
- mGridHeight = height;
- mGridRows = 1;
- mGridCols = 1;
- } else {
- mGridWidth = gridWidth;
- mGridHeight = gridHeight;
- mGridRows = gridRows;
- mGridCols = gridCols;
- }
- mNumTiles = mGridRows * mGridCols;
-
- codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
- codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
- codecFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mNumTiles);
-
- // When we're doing tiles, set the operating rate higher as the size
- // is small, otherwise set to the normal 30fps.
- if (mNumTiles > 1) {
- codecFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, 120);
- } else {
- codecFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, 30);
- }
-
- if (useSurfaceInternally && !copyTiles) {
- // Use fixed PTS gap and disable backward frame drop
- Log.d(TAG, "Setting fixed pts gap");
- codecFormat.setLong(MediaFormat.KEY_MAX_PTS_GAP_TO_ENCODER, -1000000);
- }
-
- MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
-
- if (encoderCaps.isBitrateModeSupported(
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
- Log.d(TAG, "Setting bitrate mode to constant quality");
- Range<Integer> qualityRange = encoderCaps.getQualityRange();
- Log.d(TAG, "Quality range: " + qualityRange);
- codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ);
- codecFormat.setInteger(MediaFormat.KEY_QUALITY, (int) (qualityRange.getLower() +
- (qualityRange.getUpper() - qualityRange.getLower()) * quality / 100.0));
- } else {
- if (encoderCaps.isBitrateModeSupported(
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) {
- Log.d(TAG, "Setting bitrate mode to constant bitrate");
- codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
- } else { // assume VBR
- Log.d(TAG, "Setting bitrate mode to variable bitrate");
- codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
- }
- // Calculate the bitrate based on image dimension, max compression ratio and quality.
- // Note that we set the frame rate to the number of tiles, so the bitrate would be the
- // intended bits for one image.
- int bitrate = caps.getVideoCapabilities().getBitrateRange().clamp(
- (int) (width * height * 1.5 * 8 * MAX_COMPRESS_RATIO * quality / 100.0f));
- codecFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
- }
-
- mEncoder.setCallback(new EncoderCallback(), mHandler);
- mEncoder.configure(codecFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
-
- if (useSurfaceInternally) {
- mEncoderSurface = mEncoder.createInputSurface();
-
- mEOSTracker = new SurfaceEOSTracker(copyTiles);
-
- if (copyTiles) {
- mEncoderEglSurface = new EglWindowSurface(mEncoderSurface);
- mEncoderEglSurface.makeCurrent();
-
- mRectBlt = new EglRectBlt(
- new Texture2dProgram((inputMode == INPUT_MODE_BITMAP)
- ? Texture2dProgram.TEXTURE_2D
- : Texture2dProgram.TEXTURE_EXT),
- mWidth, mHeight);
-
- mTextureId = mRectBlt.createTextureObject();
-
- if (inputMode == INPUT_MODE_SURFACE) {
- // use single buffer mode to block on input
- mInputTexture = new SurfaceTexture(mTextureId, true);
- mInputTexture.setOnFrameAvailableListener(this);
- mInputTexture.setDefaultBufferSize(mWidth, mHeight);
- mInputSurface = new Surface(mInputTexture);
- }
-
- // make uncurrent since onFrameAvailable could be called on arbituray thread.
- // making the context current on a different thread will cause error.
- mEncoderEglSurface.makeUnCurrent();
- } else {
- mInputSurface = mEncoderSurface;
- }
- } else {
- for (int i = 0; i < INPUT_BUFFER_POOL_SIZE; i++) {
- mEmptyBuffers.add(ByteBuffer.allocateDirect(mWidth * mHeight * 3 / 2));
- }
- }
-
- mDstRect = new Rect(0, 0, mGridWidth, mGridHeight);
- mSrcRect = new Rect();
+ int quality, @InputMode int inputMode,
+ @Nullable Handler handler, @NonNull Callback cb) throws IOException {
+ super("HEIC", width, height, useGrid, quality, inputMode, handler, cb,
+ /* useBitDepth10 */ false);
+ mEncoder.setCallback(new HevcEncoderCallback(), mHandler);
+ finishSettingUpEncoder(/* useBitDepth10 */ false);
}
- private String findHevcFallback() {
+ protected static String findHevcFallback() {
String hevc = null; // first HEVC encoder
for (MediaCodecInfo info : sMCL.getCodecInfos()) {
if (!info.isEncoder()) {
@@ -394,7 +96,7 @@
continue;
}
if (caps.getEncoderCapabilities().isBitrateModeSupported(
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
// Encoder that supports CQ mode is preferred over others,
// return the first encoder that supports CQ mode.
// (No need to check if it's hw based, it's already listed in
@@ -408,508 +110,12 @@
// If no encoders support CQ, return the first HEVC encoder.
return hevc;
}
- /**
- * Copies from source frame to encoder inputs using GL. The source could be either
- * client's input surface, or the input bitmap loaded to texture.
- */
- private void copyTilesGL() {
- GLES20.glViewport(0, 0, mGridWidth, mGridHeight);
-
- for (int row = 0; row < mGridRows; row++) {
- for (int col = 0; col < mGridCols; col++) {
- int left = col * mGridWidth;
- int top = row * mGridHeight;
- mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
- try {
- mRectBlt.copyRect(mTextureId, Texture2dProgram.V_FLIP_MATRIX, mSrcRect);
- } catch (RuntimeException e) {
- // EGL copy could throw if the encoder input surface is no longer valid
- // after encoder is released. This is not an error because we're already
- // stopping (either after EOS is received or requested by client).
- if (mStopping.get()) {
- return;
- }
- throw e;
- }
- mEncoderEglSurface.setPresentationTime(
- 1000 * computePresentationTime(mInputIndex++));
- mEncoderEglSurface.swapBuffers();
- }
- }
- }
-
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- synchronized (this) {
- if (mEncoderEglSurface == null) {
- return;
- }
-
- mEncoderEglSurface.makeCurrent();
-
- surfaceTexture.updateTexImage();
- surfaceTexture.getTransformMatrix(mTmpMatrix);
-
- long timestampNs = surfaceTexture.getTimestamp();
-
- if (DEBUG) Log.d(TAG, "onFrameAvailable: timestampUs " + (timestampNs / 1000));
-
- boolean takeFrame = mEOSTracker.updateLastInputAndEncoderTime(timestampNs,
- computePresentationTime(mInputIndex + mNumTiles - 1));
-
- if (takeFrame) {
- copyTilesGL();
- }
-
- surfaceTexture.releaseTexImage();
-
- // make uncurrent since the onFrameAvailable could be called on arbituray thread.
- // making the context current on a different thread will cause error.
- mEncoderEglSurface.makeUnCurrent();
- }
- }
-
- /**
- * Start the encoding process.
- */
- public void start() {
- mEncoder.start();
- }
-
- /**
- * Add one YUV buffer to be encoded. This might block if the encoder can't process the input
- * buffers fast enough.
- *
- * After the call returns, the client can reuse the data array.
- *
- * @param format The YUV format as defined in {@link android.graphics.ImageFormat}, currently
- * only support YUV_420_888.
- *
- * @param data byte array containing the YUV data. If the format has more than one planes,
- * they must be concatenated.
- */
- public void addYuvBuffer(int format, @NonNull byte[] data) {
- if (mInputMode != INPUT_MODE_BUFFER) {
- throw new IllegalStateException(
- "addYuvBuffer is only allowed in buffer input mode");
- }
- if (data == null || data.length != mWidth * mHeight * 3 / 2) {
- throw new IllegalArgumentException("invalid data");
- }
- addYuvBufferInternal(data);
- }
-
- /**
- * Retrieves the input surface for encoding.
- *
- * Will only return valid value if configured to use surface input.
- */
- public @NonNull Surface getInputSurface() {
- if (mInputMode != INPUT_MODE_SURFACE) {
- throw new IllegalStateException(
- "getInputSurface is only allowed in surface input mode");
- }
- return mInputSurface;
- }
-
- /**
- * Sets the timestamp (in nano seconds) of the last input frame to encode. Frames with
- * timestamps larger than the specified value will not be encoded. However, if a frame
- * already started encoding when this is set, all tiles within that frame will be encoded.
- *
- * This method only applies when surface is used.
- */
- public void setEndOfInputStreamTimestamp(long timestampNs) {
- if (mInputMode != INPUT_MODE_SURFACE) {
- throw new IllegalStateException(
- "setEndOfInputStreamTimestamp is only allowed in surface input mode");
- }
- if (mEOSTracker != null) {
- mEOSTracker.updateInputEOSTime(timestampNs);
- }
- }
-
- /**
- * Adds one bitmap to be encoded.
- */
- public void addBitmap(@NonNull Bitmap bitmap) {
- if (mInputMode != INPUT_MODE_BITMAP) {
- throw new IllegalStateException("addBitmap is only allowed in bitmap input mode");
- }
-
- boolean takeFrame = mEOSTracker.updateLastInputAndEncoderTime(
- computePresentationTime(mInputIndex) * 1000,
- computePresentationTime(mInputIndex + mNumTiles - 1));
-
- if (!takeFrame) return;
-
- synchronized (this) {
- if (mEncoderEglSurface == null) {
- return;
- }
-
- mEncoderEglSurface.makeCurrent();
-
- mRectBlt.loadTexture(mTextureId, bitmap);
-
- copyTilesGL();
-
- // make uncurrent since the onFrameAvailable could be called on arbituray thread.
- // making the context current on a different thread will cause error.
- mEncoderEglSurface.makeUnCurrent();
- }
- }
-
- /**
- * Sends input EOS to the encoder. Result will be notified asynchronously via
- * {@link Callback#onComplete(HeifEncoder)} if encoder reaches EOS without error, or
- * {@link Callback#onError(HeifEncoder, CodecException)} otherwise.
- */
- public void stopAsync() {
- if (mInputMode == INPUT_MODE_BITMAP) {
- // here we simply set the EOS timestamp to 0, so that the cut off will be the last
- // bitmap ever added.
- mEOSTracker.updateInputEOSTime(0);
- } else if (mInputMode == INPUT_MODE_BUFFER) {
- addYuvBufferInternal(null);
- }
- }
-
- /**
- * Generates the presentation time for input frame N, in microseconds.
- * The timestamp advances 1 sec for every whole frame.
- */
- private long computePresentationTime(int frameIndex) {
- return 132 + (long)frameIndex * 1000000 / mNumTiles;
- }
-
- /**
- * Obtains one empty input buffer and copies the data into it. Before input
- * EOS is sent, this would block until the data is copied. After input EOS
- * is sent, this would return immediately.
- */
- private void addYuvBufferInternal(@Nullable byte[] data) {
- ByteBuffer buffer = acquireEmptyBuffer();
- if (buffer == null) {
- return;
- }
- buffer.clear();
- if (data != null) {
- buffer.put(data);
- }
- buffer.flip();
- synchronized (mFilledBuffers) {
- mFilledBuffers.add(buffer);
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- maybeCopyOneTileYUV();
- }
- });
- }
-
- /**
- * Routine to copy one tile if we have both input and codec buffer available.
- *
- * Must be called on the handler looper that also handles the MediaCodec callback.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- void maybeCopyOneTileYUV() {
- ByteBuffer currentBuffer;
- while ((currentBuffer = getCurrentBuffer()) != null && !mCodecInputBuffers.isEmpty()) {
- int index = mCodecInputBuffers.remove(0);
-
- // 0-length input means EOS.
- boolean inputEOS = (mInputIndex % mNumTiles == 0) && (currentBuffer.remaining() == 0);
-
- if (!inputEOS) {
- Image image = mEncoder.getInputImage(index);
- int left = mGridWidth * (mInputIndex % mGridCols);
- int top = mGridHeight * (mInputIndex / mGridCols % mGridRows);
- mSrcRect.set(left, top, left + mGridWidth, top + mGridHeight);
- copyOneTileYUV(currentBuffer, image, mWidth, mHeight, mSrcRect, mDstRect);
- }
-
- mEncoder.queueInputBuffer(index, 0,
- inputEOS ? 0 : mEncoder.getInputBuffer(index).capacity(),
- computePresentationTime(mInputIndex++),
- inputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
- if (inputEOS || mInputIndex % mNumTiles == 0) {
- returnEmptyBufferAndNotify(inputEOS);
- }
- }
- }
-
- /**
- * Copies from a rect from src buffer to dst image.
- * TOOD: This will be replaced by JNI.
- */
- private static void copyOneTileYUV(
- ByteBuffer srcBuffer, Image dstImage,
- int srcWidth, int srcHeight,
- Rect srcRect, Rect dstRect) {
- if (srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height()) {
- throw new IllegalArgumentException("src and dst rect size are different!");
- }
- if (srcWidth % 2 != 0 || srcHeight % 2 != 0 ||
- srcRect.left % 2 != 0 || srcRect.top % 2 != 0 ||
- srcRect.right % 2 != 0 || srcRect.bottom % 2 != 0 ||
- dstRect.left % 2 != 0 || dstRect.top % 2 != 0 ||
- dstRect.right % 2 != 0 || dstRect.bottom % 2 != 0) {
- throw new IllegalArgumentException("src or dst are not aligned!");
- }
-
- Image.Plane[] planes = dstImage.getPlanes();
- for (int n = 0; n < planes.length; n++) {
- ByteBuffer dstBuffer = planes[n].getBuffer();
- int colStride = planes[n].getPixelStride();
- int copyWidth = Math.min(srcRect.width(), srcWidth - srcRect.left);
- int copyHeight = Math.min(srcRect.height(), srcHeight - srcRect.top);
- int srcPlanePos = 0, div = 1;
- if (n > 0) {
- div = 2;
- srcPlanePos = srcWidth * srcHeight * (n + 3) / 4;
- }
- for (int i = 0; i < copyHeight / div; i++) {
- srcBuffer.position(srcPlanePos +
- (i + srcRect.top / div) * srcWidth / div + srcRect.left / div);
- dstBuffer.position((i + dstRect.top / div) * planes[n].getRowStride()
- + dstRect.left * colStride / div);
-
- for (int j = 0; j < copyWidth / div; j++) {
- dstBuffer.put(srcBuffer.get());
- if (colStride > 1 && j != copyWidth / div - 1) {
- dstBuffer.position(dstBuffer.position() + colStride - 1);
- }
- }
- }
- }
- }
-
- private ByteBuffer acquireEmptyBuffer() {
- synchronized (mEmptyBuffers) {
- // wait for an empty input buffer first
- while (!mInputEOS && mEmptyBuffers.isEmpty()) {
- try {
- mEmptyBuffers.wait();
- } catch (InterruptedException e) {}
- }
-
- // if already EOS, return null to stop further encoding.
- return mInputEOS ? null : mEmptyBuffers.remove(0);
- }
- }
-
- /**
- * Routine to get the current input buffer to copy from.
- * Only called on callback handler thread.
- */
- private ByteBuffer getCurrentBuffer() {
- if (!mInputEOS && mCurrentBuffer == null) {
- synchronized (mFilledBuffers) {
- mCurrentBuffer = mFilledBuffers.isEmpty() ?
- null : mFilledBuffers.remove(0);
- }
- }
- return mInputEOS ? null : mCurrentBuffer;
- }
-
- /**
- * Routine to put the consumed input buffer back into the empty buffer pool.
- * Only called on callback handler thread.
- */
- private void returnEmptyBufferAndNotify(boolean inputEOS) {
- synchronized (mEmptyBuffers) {
- mInputEOS |= inputEOS;
- mEmptyBuffers.add(mCurrentBuffer);
- mEmptyBuffers.notifyAll();
- }
- mCurrentBuffer = null;
- }
-
- /**
- * Routine to release all resources. Must be run on the same looper that
- * handles the MediaCodec callbacks.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- void stopInternal() {
- if (DEBUG) Log.d(TAG, "stopInternal");
-
- // set stopping, so that the tile copy would bail out
- // if it hits failure after this point.
- mStopping.set(true);
-
- // after start, mEncoder is only accessed on handler, so no need to sync.
- try {
- if (mEncoder != null) {
- mEncoder.stop();
- mEncoder.release();
- }
- } catch (Exception e) {
- } finally {
- mEncoder = null;
- }
-
- // unblock the addBuffer() if we're tearing down before EOS is sent.
- synchronized (mEmptyBuffers) {
- mInputEOS = true;
- mEmptyBuffers.notifyAll();
- }
-
- // Clean up surface and Egl related refs. This lock must come after encoder
- // release. When we're closing, we insert stopInternal() at the front of queue
- // so that the shutdown can be processed promptly, this means there might be
- // some output available requests queued after this. As the tile copies trying
- // to finish the current frame, there is a chance is might get stuck because
- // those outputs were not returned. Shutting down the encoder will make break
- // the tile copier out of that.
- synchronized(this) {
- try {
- if (mRectBlt != null) {
- mRectBlt.release(false);
- }
- } catch (Exception e) {
- } finally {
- mRectBlt = null;
- }
-
- try {
- if (mEncoderEglSurface != null) {
- // Note that this frees mEncoderSurface too. If mEncoderEglSurface is not
- // there, client is responsible to release the input surface it got from us,
- // we don't release mEncoderSurface here.
- mEncoderEglSurface.release();
- }
- } catch (Exception e) {
- } finally {
- mEncoderEglSurface = null;
- }
-
- try {
- if (mInputTexture != null) {
- mInputTexture.release();
- }
- } catch (Exception e) {
- } finally {
- mInputTexture = null;
- }
- }
- }
-
- /**
- * This class handles EOS for surface or bitmap inputs.
- *
- * When encoding from surface or bitmap, we can't call {@link MediaCodec#signalEndOfInputStream()}
- * immediately after input is drawn, since this could drop all pending frames in the
- * buffer queue. When there are tiles, this could leave us a partially encoded image.
- *
- * So here we track the EOS status by timestamps, and only signal EOS to the encoder
- * when we collected all images we need.
- *
- * Since this is updated from multiple threads ({@link #setEndOfInputStreamTimestamp(long)},
- * {@link EncoderCallback#onOutputBufferAvailable(MediaCodec, int, BufferInfo)},
- * {@link #addBitmap(Bitmap)} and {@link #onFrameAvailable(SurfaceTexture)}), it must be fully
- * synchronized.
- *
- * Note that when buffer input is used, the EOS flag is set in
- * {@link EncoderCallback#onInputBufferAvailable(MediaCodec, int)} and this class is not used.
- */
- private class SurfaceEOSTracker {
- private static final boolean DEBUG_EOS = false;
-
- final boolean mCopyTiles;
- long mInputEOSTimeNs = -1;
- long mLastInputTimeNs = -1;
- long mEncoderEOSTimeUs = -1;
- long mLastEncoderTimeUs = -1;
- long mLastOutputTimeUs = -1;
- boolean mSignaled;
-
- SurfaceEOSTracker(boolean copyTiles) {
- mCopyTiles = copyTiles;
- }
-
- synchronized void updateInputEOSTime(long timestampNs) {
- if (DEBUG_EOS) Log.d(TAG, "updateInputEOSTime: " + timestampNs);
-
- if (mCopyTiles) {
- if (mInputEOSTimeNs < 0) {
- mInputEOSTimeNs = timestampNs;
- }
- } else {
- if (mEncoderEOSTimeUs < 0) {
- mEncoderEOSTimeUs = timestampNs / 1000;
- }
- }
- updateEOSLocked();
- }
-
- synchronized boolean updateLastInputAndEncoderTime(long inputTimeNs, long encoderTimeUs) {
- if (DEBUG_EOS) Log.d(TAG,
- "updateLastInputAndEncoderTime: " + inputTimeNs + ", " + encoderTimeUs);
-
- boolean shouldTakeFrame = mInputEOSTimeNs < 0 || inputTimeNs <= mInputEOSTimeNs;
- if (shouldTakeFrame) {
- mLastEncoderTimeUs = encoderTimeUs;
- }
- mLastInputTimeNs = inputTimeNs;
- updateEOSLocked();
- return shouldTakeFrame;
- }
-
- synchronized void updateLastOutputTime(long outputTimeUs) {
- if (DEBUG_EOS) Log.d(TAG, "updateLastOutputTime: " + outputTimeUs);
-
- mLastOutputTimeUs = outputTimeUs;
- updateEOSLocked();
- }
-
- private void updateEOSLocked() {
- if (mSignaled) {
- return;
- }
- if (mEncoderEOSTimeUs < 0) {
- if (mInputEOSTimeNs >= 0 && mLastInputTimeNs >= mInputEOSTimeNs) {
- if (mLastEncoderTimeUs < 0) {
- doSignalEOSLocked();
- return;
- }
- // mEncoderEOSTimeUs tracks the timestamp of the last output buffer we
- // will wait for. When that buffer arrives, encoder will be signalled EOS.
- mEncoderEOSTimeUs = mLastEncoderTimeUs;
- if (DEBUG_EOS) Log.d(TAG,
- "updateEOSLocked: mEncoderEOSTimeUs " + mEncoderEOSTimeUs);
- }
- }
- if (mEncoderEOSTimeUs >= 0 && mEncoderEOSTimeUs <= mLastOutputTimeUs) {
- doSignalEOSLocked();
- }
- }
-
- private void doSignalEOSLocked() {
- if (DEBUG_EOS) Log.d(TAG, "doSignalEOSLocked");
-
- mHandler.post(new Runnable() {
- @Override public void run() {
- if (mEncoder != null) {
- mEncoder.signalEndOfInputStream();
- }
- }
- });
-
- mSignaled = true;
- }
- }
/**
* MediaCodec callback for HEVC encoding.
*/
@SuppressWarnings("WeakerAccess") /* synthetic access */
- class EncoderCallback extends MediaCodec.Callback {
- private boolean mOutputEOS;
-
+ protected class HevcEncoderCallback extends EncoderCallback {
@Override
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
if (codec != mEncoder) return;
@@ -917,7 +123,7 @@
if (DEBUG) Log.d(TAG, "onOutputFormatChanged: " + format);
if (!MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC.equals(
- format.getString(MediaFormat.KEY_MIME))) {
+ format.getString(MediaFormat.KEY_MIME))) {
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
format.setInteger(MediaFormat.KEY_WIDTH, mWidth);
format.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
@@ -932,85 +138,5 @@
mCallback.onOutputFormatChanged(HeifEncoder.this, format);
}
-
- @Override
- public void onInputBufferAvailable(MediaCodec codec, int index) {
- if (codec != mEncoder || mInputEOS) return;
-
- if (DEBUG) Log.d(TAG, "onInputBufferAvailable: " + index);
- mCodecInputBuffers.add(index);
- maybeCopyOneTileYUV();
- }
-
- @Override
- public void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info) {
- if (codec != mEncoder || mOutputEOS) return;
-
- if (DEBUG) {
- Log.d(TAG, "onOutputBufferAvailable: " + index
- + ", time " + info.presentationTimeUs
- + ", size " + info.size
- + ", flags " + info.flags);
- }
-
- if ((info.size > 0) && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0)) {
- ByteBuffer outputBuffer = codec.getOutputBuffer(index);
-
- // reset position as addBuffer() modifies it
- outputBuffer.position(info.offset);
- outputBuffer.limit(info.offset + info.size);
-
- if (mEOSTracker != null) {
- mEOSTracker.updateLastOutputTime(info.presentationTimeUs);
- }
-
- mCallback.onDrainOutputBuffer(HeifEncoder.this, outputBuffer);
- }
-
- mOutputEOS |= ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
-
- codec.releaseOutputBuffer(index, false);
-
- if (mOutputEOS) {
- stopAndNotify(null);
- }
- }
-
- @Override
- public void onError(MediaCodec codec, CodecException e) {
- if (codec != mEncoder) return;
-
- Log.e(TAG, "onError: " + e);
- stopAndNotify(e);
- }
-
- private void stopAndNotify(@Nullable CodecException e) {
- stopInternal();
- if (e == null) {
- mCallback.onComplete(HeifEncoder.this);
- } else {
- mCallback.onError(HeifEncoder.this, e);
- }
- }
}
-
- @Override
- public void close() {
- // unblock the addBuffer() if we're tearing down before EOS is sent.
- synchronized (mEmptyBuffers) {
- mInputEOS = true;
- mEmptyBuffers.notifyAll();
- }
-
- mHandler.postAtFrontOfQueue(new Runnable() {
- @Override
- public void run() {
- try {
- stopInternal();
- } catch (Exception e) {
- // We don't want to crash when closing.
- }
- }
- });
- }
-}
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
index 4279d6f..a64aa21 100644
--- a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
@@ -32,6 +32,7 @@
import android.view.Surface;
import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@@ -78,42 +79,17 @@
*
* <p>Please refer to the documentations on individual methods for the exact usage.
*/
-public final class HeifWriter implements AutoCloseable {
+@SuppressWarnings("HiddenSuperclass")
+public final class HeifWriter extends WriterBase {
private static final String TAG = "HeifWriter";
private static final boolean DEBUG = false;
- private static final int MUXER_DATA_FLAG = 16;
-
- private final @InputMode int mInputMode;
- private final HandlerThread mHandlerThread;
- private final Handler mHandler;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- int mNumTiles;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mRotation;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mMaxImages;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final int mPrimaryIndex;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- final ResultWaiter mResultWaiter = new ResultWaiter();
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- MediaMuxer mMuxer;
- private HeifEncoder mHeifEncoder;
- final AtomicBoolean mMuxerStarted = new AtomicBoolean(false);
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- int[] mTrackIndexArray;
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- int mOutputIndex;
- private boolean mStarted;
-
- private final List<Pair<Integer, ByteBuffer>> mExifList = new ArrayList<>();
/**
* The input mode where the client adds input buffers with YUV data.
*
* @see #addYuvBuffer(int, byte[])
*/
- public static final int INPUT_MODE_BUFFER = 0;
+ public static final int INPUT_MODE_BUFFER = WriterBase.INPUT_MODE_BUFFER;
/**
* The input mode where the client renders the images to an input Surface
@@ -126,18 +102,18 @@
*
* @see #getInputSurface()
*/
- public static final int INPUT_MODE_SURFACE = 1;
+ public static final int INPUT_MODE_SURFACE = WriterBase.INPUT_MODE_SURFACE;
/**
* The input mode where the client adds bitmaps.
*
* @see #addBitmap(Bitmap)
*/
- public static final int INPUT_MODE_BITMAP = 2;
+ public static final int INPUT_MODE_BITMAP = WriterBase.INPUT_MODE_BITMAP;
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef({
- INPUT_MODE_BUFFER, INPUT_MODE_SURFACE, INPUT_MODE_BITMAP,
+ INPUT_MODE_BUFFER, INPUT_MODE_SURFACE, INPUT_MODE_BITMAP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InputMode {}
@@ -162,13 +138,15 @@
* Construct a Builder with output specified by its path.
*
* @param path Path of the file to be written.
- * @param width Width of the image.
- * @param height Height of the image.
+ * @param width Width of the image in number of pixels.
+ * @param height Height of the image in number of pixels.
* @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
* {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
*/
public Builder(@NonNull String path,
- int width, int height, @InputMode int inputMode) {
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
this(path, null, width, height, inputMode);
}
@@ -176,21 +154,22 @@
* Construct a Builder with output specified by its file descriptor.
*
* @param fd File descriptor of the file to be written.
- * @param width Width of the image.
- * @param height Height of the image.
+ * @param width Width of the image in number of pixels.
+ * @param height Height of the image in number of pixels.
* @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
* {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
*/
public Builder(@NonNull FileDescriptor fd,
- int width, int height, @InputMode int inputMode) {
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
this(null, fd, width, height, inputMode);
}
private Builder(String path, FileDescriptor fd,
- int width, int height, @InputMode int inputMode) {
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("Invalid image size: " + width + "x" + height);
- }
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @InputMode int inputMode) {
mPath = path;
mFd = fd;
mWidth = width;
@@ -201,11 +180,11 @@
/**
* Set the image rotation in degrees.
*
- * @param rotation Rotation angle (clockwise) of the image, must be 0, 90, 180 or 270.
- * Default is 0.
+ * @param rotation Rotation angle in degrees (clockwise) of the image, must be 0, 90,
+ * 180 or 270. Default is 0.
* @return this Builder object.
*/
- public Builder setRotation(int rotation) {
+ public @NonNull Builder setRotation(@IntRange(from = 0) int rotation) {
if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
throw new IllegalArgumentException("Invalid rotation angle: " + rotation);
}
@@ -220,7 +199,7 @@
* automatically chosen. Default is to enable.
* @return this Builder object.
*/
- public Builder setGridEnabled(boolean gridEnabled) {
+ public @NonNull Builder setGridEnabled(boolean gridEnabled) {
mGridEnabled = gridEnabled;
return this;
}
@@ -232,7 +211,7 @@
* quality supported by this implementation. Default is 100.
* @return this Builder object.
*/
- public Builder setQuality(int quality) {
+ public @NonNull Builder setQuality(@IntRange(from = 0, to = 100) int quality) {
if (quality < 0 || quality > 100) {
throw new IllegalArgumentException("Invalid quality: " + quality);
}
@@ -251,7 +230,7 @@
* Default is 1.
* @return this Builder object.
*/
- public Builder setMaxImages(int maxImages) {
+ public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) {
if (maxImages <= 0) {
throw new IllegalArgumentException("Invalid maxImage: " + maxImages);
}
@@ -266,10 +245,7 @@
* range [0, maxImages - 1] inclusive. Default is 0.
* @return this Builder object.
*/
- public Builder setPrimaryIndex(int primaryIndex) {
- if (primaryIndex < 0) {
- throw new IllegalArgumentException("Invalid primaryIndex: " + primaryIndex);
- }
+ public @NonNull Builder setPrimaryIndex(@IntRange(from = 0) int primaryIndex) {
mPrimaryIndex = primaryIndex;
return this;
}
@@ -282,7 +258,7 @@
* writer. Default is null.
* @return this Builder object.
*/
- public Builder setHandler(@Nullable Handler handler) {
+ public @NonNull Builder setHandler(@Nullable Handler handler) {
mHandler = handler;
return this;
}
@@ -294,428 +270,46 @@
* @throws IOException if failed to create the writer, possibly due to failure to create
* {@link android.media.MediaMuxer} or {@link android.media.MediaCodec}.
*/
- public HeifWriter build() throws IOException {
+ public @NonNull HeifWriter build() throws IOException {
return new HeifWriter(mPath, mFd, mWidth, mHeight, mRotation, mGridEnabled, mQuality,
- mMaxImages, mPrimaryIndex, mInputMode, mHandler);
+ mMaxImages, mPrimaryIndex, mInputMode, mHandler);
}
}
@SuppressLint("WrongConstant")
@SuppressWarnings("WeakerAccess") /* synthetic access */
HeifWriter(@NonNull String path,
- @NonNull FileDescriptor fd,
- int width,
- int height,
- int rotation,
- boolean gridEnabled,
- int quality,
- int maxImages,
- int primaryIndex,
- @InputMode int inputMode,
- @Nullable Handler handler) throws IOException {
- if (primaryIndex >= maxImages) {
- throw new IllegalArgumentException(
- "Invalid maxImages (" + maxImages + ") or primaryIndex (" + primaryIndex + ")");
- }
+ @NonNull FileDescriptor fd,
+ int width,
+ int height,
+ int rotation,
+ boolean gridEnabled,
+ int quality,
+ int maxImages,
+ int primaryIndex,
+ @InputMode int inputMode,
+ @Nullable Handler handler) throws IOException {
+ super(rotation, inputMode, maxImages, primaryIndex, gridEnabled, quality,
+ handler, /* highBitDepthEnabled */ false);
if (DEBUG) {
Log.d(TAG, "width: " + width
- + ", height: " + height
- + ", rotation: " + rotation
- + ", gridEnabled: " + gridEnabled
- + ", quality: " + quality
- + ", maxImages: " + maxImages
- + ", primaryIndex: " + primaryIndex
- + ", inputMode: " + inputMode);
+ + ", height: " + height
+ + ", rotation: " + rotation
+ + ", gridEnabled: " + gridEnabled
+ + ", quality: " + quality
+ + ", maxImages: " + maxImages
+ + ", primaryIndex: " + primaryIndex
+ + ", inputMode: " + inputMode);
}
// set to 1 initially, and wait for output format to know for sure
mNumTiles = 1;
- mRotation = rotation;
- mInputMode = inputMode;
- mMaxImages = maxImages;
- mPrimaryIndex = primaryIndex;
-
- Looper looper = (handler != null) ? handler.getLooper() : null;
- if (looper == null) {
- mHandlerThread = new HandlerThread("HeifEncoderThread",
- Process.THREAD_PRIORITY_FOREGROUND);
- mHandlerThread.start();
- looper = mHandlerThread.getLooper();
- } else {
- mHandlerThread = null;
- }
- mHandler = new Handler(looper);
-
mMuxer = (path != null) ? new MediaMuxer(path, MUXER_OUTPUT_HEIF)
- : new MediaMuxer(fd, MUXER_OUTPUT_HEIF);
+ : new MediaMuxer(fd, MUXER_OUTPUT_HEIF);
- mHeifEncoder = new HeifEncoder(width, height, gridEnabled, quality,
- mInputMode, mHandler, new HeifCallback());
+ mEncoder = new HeifEncoder(width, height, gridEnabled, quality,
+ mInputMode, mHandler, new WriterCallback());
}
-
- /**
- * Start the heif writer. Can only be called once.
- *
- * @throws IllegalStateException if called more than once.
- */
- public void start() {
- checkStarted(false);
- mStarted = true;
- mHeifEncoder.start();
- }
-
- /**
- * Add one YUV buffer to the heif file.
- *
- * @param format The YUV format as defined in {@link android.graphics.ImageFormat}, currently
- * only support YUV_420_888.
- *
- * @param data byte array containing the YUV data. If the format has more than one planes,
- * they must be concatenated.
- *
- * @throws IllegalStateException if not started or not configured to use buffer input.
- */
- public void addYuvBuffer(int format, @NonNull byte[] data) {
- checkStartedAndMode(INPUT_MODE_BUFFER);
- synchronized (this) {
- if (mHeifEncoder != null) {
- mHeifEncoder.addYuvBuffer(format, data);
- }
- }
- }
-
- /**
- * Retrieves the input surface for encoding.
- *
- * @return the input surface if configured to use surface input.
- *
- * @throws IllegalStateException if called after start or not configured to use surface input.
- */
- public @NonNull Surface getInputSurface() {
- checkStarted(false);
- checkMode(INPUT_MODE_SURFACE);
- return mHeifEncoder.getInputSurface();
- }
-
- /**
- * Set the timestamp (in nano seconds) of the last input frame to encode.
- *
- * This call is only valid for surface input. Client can use this to stop the heif writer
- * earlier before the maximum number of images are written. If not called, the writer will
- * only stop when the maximum number of images are written.
- *
- * @param timestampNs timestamp (in nano seconds) of the last frame that will be written to the
- * heif file. Frames with timestamps larger than the specified value will not
- * be written. However, if a frame already started encoding when this is set,
- * all tiles within that frame will be encoded.
- *
- * @throws IllegalStateException if not started or not configured to use surface input.
- */
- public void setInputEndOfStreamTimestamp(long timestampNs) {
- checkStartedAndMode(INPUT_MODE_SURFACE);
- synchronized (this) {
- if (mHeifEncoder != null) {
- mHeifEncoder.setEndOfInputStreamTimestamp(timestampNs);
- }
- }
- }
-
- /**
- * Add one bitmap to the heif file.
- *
- * @param bitmap the bitmap to be added to the file.
- * @throws IllegalStateException if not started or not configured to use bitmap input.
- */
- public void addBitmap(@NonNull Bitmap bitmap) {
- checkStartedAndMode(INPUT_MODE_BITMAP);
- synchronized (this) {
- if (mHeifEncoder != null) {
- mHeifEncoder.addBitmap(bitmap);
- }
- }
- }
-
- /**
- * Add Exif data for the specified image. The data must be a valid Exif data block,
- * starting with "Exif\0\0" followed by the TIFF header (See JEITA CP-3451C Section 4.5.2.)
- *
- * @param imageIndex index of the image, must be a valid index for the max number of image
- * specified by {@link Builder#setMaxImages(int)}.
- * @param exifData byte buffer containing a Exif data block.
- * @param offset offset of the Exif data block within exifData.
- * @param length length of the Exif data block.
- */
- public void addExifData(int imageIndex, @NonNull byte[] exifData, int offset, int length) {
- checkStarted(true);
-
- ByteBuffer buffer = ByteBuffer.allocateDirect(length);
- buffer.put(exifData, offset, length);
- buffer.flip();
- // Put it in a queue, as we might not be able to process it at this time.
- synchronized (mExifList) {
- mExifList.add(new Pair<Integer, ByteBuffer>(imageIndex, buffer));
- }
- processExifData();
- }
-
- @SuppressLint("WrongConstant")
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- void processExifData() {
- if (!mMuxerStarted.get()) {
- return;
- }
-
- while (true) {
- Pair<Integer, ByteBuffer> entry;
- synchronized (mExifList) {
- if (mExifList.isEmpty()) {
- return;
- }
- entry = mExifList.remove(0);
- }
- MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
- info.set(entry.second.position(), entry.second.remaining(), 0, MUXER_DATA_FLAG);
- mMuxer.writeSampleData(mTrackIndexArray[entry.first], entry.second, info);
- }
- }
-
- /**
- * Stop the heif writer synchronously. Throws exception if the writer didn't finish writing
- * successfully. Upon a success return:
- *
- * - For buffer and bitmap inputs, all images sent before stop will be written.
- *
- * - For surface input, images with timestamp on or before that specified in
- * {@link #setInputEndOfStreamTimestamp(long)} will be written. In case where
- * {@link #setInputEndOfStreamTimestamp(long)} was never called, stop will block
- * until maximum number of images are received.
- *
- * @param timeoutMs Maximum time (in microsec) to wait for the writer to complete, with zero
- * indicating waiting indefinitely.
- * @see #setInputEndOfStreamTimestamp(long)
- * @throws Exception if encountered error, in which case the output file may not be valid. In
- * particular, {@link TimeoutException} is thrown when timed out, and {@link
- * MediaCodec.CodecException} is thrown when encountered codec error.
- */
- public void stop(long timeoutMs) throws Exception {
- checkStarted(true);
- synchronized (this) {
- if (mHeifEncoder != null) {
- mHeifEncoder.stopAsync();
- }
- }
- mResultWaiter.waitForResult(timeoutMs);
- processExifData();
- closeInternal();
- }
-
- private void checkStarted(boolean requiredStarted) {
- if (mStarted != requiredStarted) {
- throw new IllegalStateException("Already started");
- }
- }
-
- private void checkMode(@InputMode int requiredMode) {
- if (mInputMode != requiredMode) {
- throw new IllegalStateException("Not valid in input mode " + mInputMode);
- }
- }
-
- private void checkStartedAndMode(@InputMode int requiredMode) {
- checkStarted(true);
- checkMode(requiredMode);
- }
-
- /**
- * Routine to stop and release writer, must be called on the same looper
- * that receives heif encoder callbacks.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- void closeInternal() {
- if (DEBUG) Log.d(TAG, "closeInternal");
- // We don't want to crash when closing, catch all exceptions.
- try {
- // Muxer could throw exceptions if stop is called without samples.
- // Don't crash in that case.
- if (mMuxer != null) {
- mMuxer.stop();
- mMuxer.release();
- }
- } catch (Exception e) {
- } finally {
- mMuxer = null;
- }
- try {
- if (mHeifEncoder != null) {
- mHeifEncoder.close();
- }
- } catch (Exception e) {
- } finally {
- synchronized (this) {
- mHeifEncoder = null;
- }
- }
- }
-
- /**
- * Callback from the heif encoder.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- class HeifCallback extends HeifEncoder.Callback {
- private boolean mEncoderStopped;
- /**
- * Upon receiving output format from the encoder, add the requested number of
- * image tracks to the muxer and start the muxer.
- */
- @Override
- public void onOutputFormatChanged(
- @NonNull HeifEncoder encoder, @NonNull MediaFormat format) {
- if (mEncoderStopped) return;
-
- if (DEBUG) {
- Log.d(TAG, "onOutputFormatChanged: " + format);
- }
- if (mTrackIndexArray != null) {
- stopAndNotify(new IllegalStateException(
- "Output format changed after muxer started"));
- return;
- }
-
- try {
- int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
- int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
- mNumTiles = gridRows * gridCols;
- } catch (NullPointerException | ClassCastException e) {
- mNumTiles = 1;
- }
-
- // add mMaxImages image tracks of the same format
- mTrackIndexArray = new int[mMaxImages];
-
- // set rotation angle
- if (mRotation > 0) {
- Log.d(TAG, "setting rotation: " + mRotation);
- mMuxer.setOrientationHint(mRotation);
- }
- for (int i = 0; i < mTrackIndexArray.length; i++) {
- // mark primary
- format.setInteger(MediaFormat.KEY_IS_DEFAULT, (i == mPrimaryIndex) ? 1 : 0);
- mTrackIndexArray[i] = mMuxer.addTrack(format);
- }
- mMuxer.start();
- mMuxerStarted.set(true);
- processExifData();
- }
-
- /**
- * Upon receiving an output buffer from the encoder (which is one image when
- * grid is not used, or one tile if grid is used), add that sample to the muxer.
- */
- @Override
- public void onDrainOutputBuffer(
- @NonNull HeifEncoder encoder, @NonNull ByteBuffer byteBuffer) {
- if (mEncoderStopped) return;
-
- if (DEBUG) {
- Log.d(TAG, "onDrainOutputBuffer: " + mOutputIndex);
- }
- if (mTrackIndexArray == null) {
- stopAndNotify(new IllegalStateException(
- "Output buffer received before format info"));
- return;
- }
-
- if (mOutputIndex < mMaxImages * mNumTiles) {
- MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
- info.set(byteBuffer.position(), byteBuffer.remaining(), 0, 0);
- mMuxer.writeSampleData(
- mTrackIndexArray[mOutputIndex / mNumTiles], byteBuffer, info);
- }
-
- mOutputIndex++;
-
- // post EOS if reached max number of images allowed.
- if (mOutputIndex == mMaxImages * mNumTiles) {
- stopAndNotify(null);
- }
- }
-
- @Override
- public void onComplete(@NonNull HeifEncoder encoder) {
- stopAndNotify(null);
- }
-
- @Override
- public void onError(@NonNull HeifEncoder encoder, @NonNull MediaCodec.CodecException e) {
- stopAndNotify(e);
- }
-
- private void stopAndNotify(@Nullable Exception error) {
- if (mEncoderStopped) return;
-
- mEncoderStopped = true;
- mResultWaiter.signalResult(error);
- }
- }
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static class ResultWaiter {
- private boolean mDone;
- private Exception mException;
-
- synchronized void waitForResult(long timeoutMs) throws Exception {
- if (timeoutMs < 0) {
- throw new IllegalArgumentException("timeoutMs is negative");
- }
- if (timeoutMs == 0) {
- while (!mDone) {
- try {
- wait();
- } catch (InterruptedException ex) {}
- }
- } else {
- final long startTimeMs = System.currentTimeMillis();
- long remainingWaitTimeMs = timeoutMs;
- // avoid early termination by "spurious" wakeup.
- while (!mDone && remainingWaitTimeMs > 0) {
- try {
- wait(remainingWaitTimeMs);
- } catch (InterruptedException ex) {}
- remainingWaitTimeMs -= (System.currentTimeMillis() - startTimeMs);
- }
- }
- if (!mDone) {
- mDone = true;
- mException = new TimeoutException("timed out waiting for result");
- }
- if (mException != null) {
- throw mException;
- }
- }
-
- synchronized void signalResult(@Nullable Exception e) {
- if (!mDone) {
- mDone = true;
- mException = e;
- notifyAll();
- }
- }
- }
-
- @Override
- public void close() {
- mHandler.postAtFrontOfQueue(new Runnable() {
- @Override
- public void run() {
- try {
- closeInternal();
- } catch (Exception e) {
- // If the client called stop() properly, any errors would have been
- // reported there. We don't want to crash when closing.
- }
- }
- });
- }
-}
+}
\ No newline at end of file
diff --git a/heifwriter/heifwriter/src/main/java/androidx/heifwriter/WriterBase.java b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/WriterBase.java
new file mode 100644
index 0000000..7f283edf
--- /dev/null
+++ b/heifwriter/heifwriter/src/main/java/androidx/heifwriter/WriterBase.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.heifwriter;
+
+import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class holds common utliities for {@link HeifWriter} and {@link AvifWriter}.
+ *
+ * @hide
+ */
+public class WriterBase implements AutoCloseable {
+ private static final String TAG = "WriterBase";
+ private static final boolean DEBUG = false;
+ private static final int MUXER_DATA_FLAG = 16;
+
+ /**
+ * The input mode where the client adds input buffers with YUV data.
+ *
+ * @see #addYuvBuffer(int, byte[])
+ */
+ protected static final int INPUT_MODE_BUFFER = 0;
+
+ /**
+ * The input mode where the client renders the images to an input Surface
+ * created by the writer.
+ *
+ * The input surface operates in single buffer mode. As a result, for use case
+ * where camera directly outputs to the input surface, this mode will not work
+ * because camera framework requires multiple buffers to operate in a pipeline
+ * fashion.
+ *
+ * @see #getInputSurface()
+ */
+ protected static final int INPUT_MODE_SURFACE = 1;
+
+ /**
+ * The input mode where the client adds bitmaps.
+ *
+ * @see #addBitmap(Bitmap)
+ */
+ protected static final int INPUT_MODE_BITMAP = 2;
+
+ /** @hide */
+ @IntDef({
+ INPUT_MODE_BUFFER, INPUT_MODE_SURFACE, INPUT_MODE_BITMAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InputMode {}
+
+ protected final @InputMode int mInputMode;
+ protected final boolean mHighBitDepthEnabled;
+ protected final HandlerThread mHandlerThread;
+ protected final Handler mHandler;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected int mNumTiles;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mRotation;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mMaxImages;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected final int mPrimaryIndex;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final ResultWaiter mResultWaiter = new ResultWaiter();
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ @NonNull protected MediaMuxer mMuxer;
+ @NonNull protected EncoderBase mEncoder;
+ final AtomicBoolean mMuxerStarted = new AtomicBoolean(false);
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ int[] mTrackIndexArray;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ int mOutputIndex;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ boolean mGridEnabled;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ int mQuality;
+ private boolean mStarted;
+
+ private final List<Pair<Integer, ByteBuffer>> mExifList = new ArrayList<>();
+
+ protected WriterBase(int rotation,
+ @InputMode int inputMode,
+ int maxImages,
+ int primaryIndex,
+ boolean gridEnabled,
+ int quality,
+ @Nullable Handler handler,
+ boolean highBitDepthEnabled) throws IOException {
+ if (primaryIndex >= maxImages) {
+ throw new IllegalArgumentException(
+ "Invalid maxImages (" + maxImages + ") or primaryIndex (" + primaryIndex + ")");
+ }
+
+ mRotation = rotation;
+ mInputMode = inputMode;
+ mMaxImages = maxImages;
+ mPrimaryIndex = primaryIndex;
+ mGridEnabled = gridEnabled;
+ mQuality = quality;
+ mHighBitDepthEnabled = highBitDepthEnabled;
+
+ Looper looper = (handler != null) ? handler.getLooper() : null;
+ if (looper == null) {
+ mHandlerThread = new HandlerThread("HeifEncoderThread",
+ Process.THREAD_PRIORITY_FOREGROUND);
+ mHandlerThread.start();
+ looper = mHandlerThread.getLooper();
+ } else {
+ mHandlerThread = null;
+ }
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * Start the heif writer. Can only be called once.
+ *
+ * @throws IllegalStateException if called more than once.
+ */
+ public void start() {
+ checkStarted(false);
+ mStarted = true;
+ mEncoder.start();
+ }
+
+ /**
+ * Add one YUV buffer to the heif file.
+ *
+ * @param format The YUV format as defined in {@link android.graphics.ImageFormat}, currently
+ * only support YUV_420_888.
+ *
+ * @param data byte array containing the YUV data. If the format has more than one planes,
+ * they must be concatenated.
+ *
+ * @throws IllegalStateException if not started or not configured to use buffer input.
+ */
+ public void addYuvBuffer(int format, @NonNull byte[] data) {
+ checkStartedAndMode(INPUT_MODE_BUFFER);
+ synchronized (this) {
+ if (mEncoder != null) {
+ mEncoder.addYuvBuffer(format, data);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the input surface for encoding.
+ *
+ * @return the input surface if configured to use surface input.
+ *
+ * @throws IllegalStateException if called after start or not configured to use surface input.
+ */
+ public @NonNull Surface getInputSurface() {
+ checkStarted(false);
+ checkMode(INPUT_MODE_SURFACE);
+ return mEncoder.getInputSurface();
+ }
+
+ /**
+ * Set the timestamp (in nano seconds) of the last input frame to encode.
+ *
+ * This call is only valid for surface input. Client can use this to stop the heif writer
+ * earlier before the maximum number of images are written. If not called, the writer will
+ * only stop when the maximum number of images are written.
+ *
+ * @param timestampNs timestamp (in nano seconds) of the last frame that will be written to the
+ * heif file. Frames with timestamps larger than the specified value will not
+ * be written. However, if a frame already started encoding when this is set,
+ * all tiles within that frame will be encoded.
+ *
+ * @throws IllegalStateException if not started or not configured to use surface input.
+ */
+ public void setInputEndOfStreamTimestamp(@IntRange(from = 0) long timestampNs) {
+ checkStartedAndMode(INPUT_MODE_SURFACE);
+ synchronized (this) {
+ if (mEncoder != null) {
+ mEncoder.setEndOfInputStreamTimestamp(timestampNs);
+ }
+ }
+ }
+
+ /**
+ * Add one bitmap to the heif file.
+ *
+ * @param bitmap the bitmap to be added to the file.
+ * @throws IllegalStateException if not started or not configured to use bitmap input.
+ */
+ public void addBitmap(@NonNull Bitmap bitmap) {
+ checkStartedAndMode(INPUT_MODE_BITMAP);
+ synchronized (this) {
+ if (mEncoder != null) {
+ mEncoder.addBitmap(bitmap);
+ }
+ }
+ }
+
+ /**
+ * Add Exif data for the specified image. The data must be a valid Exif data block,
+ * starting with "Exif\0\0" followed by the TIFF header (See JEITA CP-3451C Section 4.5.2.)
+ *
+ * @param imageIndex index of the image, must be a valid index for the max number of image
+ * specified by {@link Builder#setMaxImages(int)}.
+ * @param exifData byte buffer containing a Exif data block.
+ * @param offset offset of the Exif data block within exifData.
+ * @param length length of the Exif data block.
+ */
+ public void addExifData(int imageIndex, @NonNull byte[] exifData, int offset, int length) {
+ checkStarted(true);
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(length);
+ buffer.put(exifData, offset, length);
+ buffer.flip();
+ // Put it in a queue, as we might not be able to process it at this time.
+ synchronized (mExifList) {
+ mExifList.add(new Pair<Integer, ByteBuffer>(imageIndex, buffer));
+ }
+ processExifData();
+ }
+
+ @SuppressLint("WrongConstant")
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ void processExifData() {
+ if (!mMuxerStarted.get()) {
+ return;
+ }
+
+ while (true) {
+ Pair<Integer, ByteBuffer> entry;
+ synchronized (mExifList) {
+ if (mExifList.isEmpty()) {
+ return;
+ }
+ entry = mExifList.remove(0);
+ }
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ info.set(entry.second.position(), entry.second.remaining(), 0, MUXER_DATA_FLAG);
+ mMuxer.writeSampleData(mTrackIndexArray[entry.first], entry.second, info);
+ }
+ }
+
+ /**
+ * Stop the heif writer synchronously. Throws exception if the writer didn't finish writing
+ * successfully. Upon a success return:
+ *
+ * - For buffer and bitmap inputs, all images sent before stop will be written.
+ *
+ * - For surface input, images with timestamp on or before that specified in
+ * {@link #setInputEndOfStreamTimestamp(long)} will be written. In case where
+ * {@link #setInputEndOfStreamTimestamp(long)} was never called, stop will block
+ * until maximum number of images are received.
+ *
+ * @param timeoutMs Maximum time (in microsec) to wait for the writer to complete, with zero
+ * indicating waiting indefinitely.
+ * @see #setInputEndOfStreamTimestamp(long)
+ * @throws Exception if encountered error, in which case the output file may not be valid. In
+ * particular, {@link TimeoutException} is thrown when timed out, and {@link
+ * MediaCodec.CodecException} is thrown when encountered codec error.
+ */
+ public void stop(@IntRange(from = 0) long timeoutMs) throws Exception {
+ checkStarted(true);
+ synchronized (this) {
+ if (mEncoder != null) {
+ mEncoder.stopAsync();
+ }
+ }
+ mResultWaiter.waitForResult(timeoutMs);
+ processExifData();
+ closeInternal();
+ }
+
+ private void checkStarted(boolean requiredStarted) {
+ if (mStarted != requiredStarted) {
+ throw new IllegalStateException("Already started");
+ }
+ }
+
+ private void checkMode(@InputMode int requiredMode) {
+ if (mInputMode != requiredMode) {
+ throw new IllegalStateException("Not valid in input mode " + mInputMode);
+ }
+ }
+
+ private void checkStartedAndMode(@InputMode int requiredMode) {
+ checkStarted(true);
+ checkMode(requiredMode);
+ }
+
+ /**
+ * Routine to stop and release writer, must be called on the same looper
+ * that receives heif encoder callbacks.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ void closeInternal() {
+ if (DEBUG) Log.d(TAG, "closeInternal");
+ // We don't want to crash when closing, catch all exceptions.
+ try {
+ // Muxer could throw exceptions if stop is called without samples.
+ // Don't crash in that case.
+ if (mMuxer != null) {
+ mMuxer.stop();
+ mMuxer.release();
+ }
+ } catch (Exception e) {
+ } finally {
+ mMuxer = null;
+ }
+ try {
+ if (mEncoder != null) {
+ mEncoder.close();
+ }
+ } catch (Exception e) {
+ } finally {
+ synchronized (this) {
+ mEncoder = null;
+ }
+ }
+ }
+
+ /**
+ * Callback from the encoder.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ protected class WriterCallback extends EncoderBase.Callback {
+ private boolean mEncoderStopped;
+ /**
+ * Upon receiving output format from the encoder, add the requested number of
+ * image tracks to the muxer and start the muxer.
+ */
+ @Override
+ public void onOutputFormatChanged(
+ @NonNull EncoderBase encoder, @NonNull MediaFormat format) {
+ if (mEncoderStopped) return;
+
+ if (DEBUG) {
+ Log.d(TAG, "onOutputFormatChanged: " + format);
+ }
+ if (mTrackIndexArray != null) {
+ stopAndNotify(new IllegalStateException(
+ "Output format changed after muxer started"));
+ return;
+ }
+
+ try {
+ int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
+ int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
+ mNumTiles = gridRows * gridCols;
+ } catch (NullPointerException | ClassCastException e) {
+ mNumTiles = 1;
+ }
+
+ // add mMaxImages image tracks of the same format
+ mTrackIndexArray = new int[mMaxImages];
+
+ // set rotation angle
+ if (mRotation > 0) {
+ Log.d(TAG, "setting rotation: " + mRotation);
+ mMuxer.setOrientationHint(mRotation);
+ }
+ for (int i = 0; i < mTrackIndexArray.length; i++) {
+ // mark primary
+ format.setInteger(MediaFormat.KEY_IS_DEFAULT, (i == mPrimaryIndex) ? 1 : 0);
+ mTrackIndexArray[i] = mMuxer.addTrack(format);
+ }
+ mMuxer.start();
+ mMuxerStarted.set(true);
+ processExifData();
+ }
+
+ /**
+ * Upon receiving an output buffer from the encoder (which is one image when
+ * grid is not used, or one tile if grid is used), add that sample to the muxer.
+ */
+ @Override
+ public void onDrainOutputBuffer(
+ @NonNull EncoderBase encoder, @NonNull ByteBuffer byteBuffer) {
+ if (mEncoderStopped) return;
+
+ if (DEBUG) {
+ Log.d(TAG, "onDrainOutputBuffer: " + mOutputIndex);
+ }
+ if (mTrackIndexArray == null) {
+ stopAndNotify(new IllegalStateException(
+ "Output buffer received before format info"));
+ return;
+ }
+
+ if (mOutputIndex < mMaxImages * mNumTiles) {
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ info.set(byteBuffer.position(), byteBuffer.remaining(), 0, 0);
+ mMuxer.writeSampleData(
+ mTrackIndexArray[mOutputIndex / mNumTiles], byteBuffer, info);
+ }
+
+ mOutputIndex++;
+
+ // post EOS if reached max number of images allowed.
+ if (mOutputIndex == mMaxImages * mNumTiles) {
+ stopAndNotify(null);
+ }
+ }
+
+ @Override
+ public void onComplete(@NonNull EncoderBase encoder) {
+ stopAndNotify(null);
+ }
+
+ @Override
+ public void onError(@NonNull EncoderBase encoder, @NonNull MediaCodec.CodecException e) {
+ stopAndNotify(e);
+ }
+
+ private void stopAndNotify(@Nullable Exception error) {
+ if (mEncoderStopped) return;
+
+ mEncoderStopped = true;
+ mResultWaiter.signalResult(error);
+ }
+ }
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ static class ResultWaiter {
+ private boolean mDone;
+ private Exception mException;
+
+ synchronized void waitForResult(long timeoutMs) throws Exception {
+ if (timeoutMs < 0) {
+ throw new IllegalArgumentException("timeoutMs is negative");
+ }
+ if (timeoutMs == 0) {
+ while (!mDone) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {}
+ }
+ } else {
+ final long startTimeMs = System.currentTimeMillis();
+ long remainingWaitTimeMs = timeoutMs;
+ // avoid early termination by "spurious" wakeup.
+ while (!mDone && remainingWaitTimeMs > 0) {
+ try {
+ wait(remainingWaitTimeMs);
+ } catch (InterruptedException ex) {}
+ remainingWaitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+ }
+ }
+ if (!mDone) {
+ mDone = true;
+ mException = new TimeoutException("timed out waiting for result");
+ }
+ if (mException != null) {
+ throw mException;
+ }
+ }
+
+ synchronized void signalResult(@Nullable Exception e) {
+ if (!mDone) {
+ mDone = true;
+ mException = e;
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ mHandler.postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ closeInternal();
+ } catch (Exception e) {
+ // If the client called stop() properly, any errors would have been
+ // reported there. We don't want to crash when closing.
+ }
+ }
+ });
+ }
+
+ /*
+ * Gets rotation.
+ */
+ public int getRotation() {
+ return mRotation;
+ }
+
+ /*
+ * Returns true if grid is enabled.
+ */
+ public boolean isGridEnabled() {
+ return mGridEnabled;
+ }
+
+ /*
+ * Gets configured quality.
+ */
+ public int getQuality() {
+ return mQuality;
+ }
+
+ /*
+ * Gets number of maximum images.
+ */
+ public int getMaxImages() {
+ return mMaxImages;
+ }
+
+ /*
+ * Gets index of the primary image.
+ */
+ public int getPrimaryIndex() {
+ return mPrimaryIndex;
+ }
+
+ /*
+ * Gets handler.
+ *
+ * The result is the same as clients' input from setHandler() method.
+ * If not null, client will receive all callbacks on the handler's looper.
+ * Otherwise, client will receive callbacks on the current looper.
+ */
+ public @Nullable Handler getHandler() {
+ return mHandler;
+ }
+
+ /*
+ * Returns true if high bit-depth is enabled.
+ */
+ public boolean isHighBitDepthEnabled() {
+ return mHighBitDepthEnabled;
+ }
+}
\ No newline at end of file
diff --git a/leanback/leanback/api/api_lint.ignore b/leanback/leanback/api/api_lint.ignore
index a72d2ae..a568a48 100644
--- a/leanback/leanback/api/api_lint.ignore
+++ b/leanback/leanback/api/api_lint.ignore
@@ -147,8 +147,6 @@
Invalid nullability on parameter `view` in method `onViewCreated`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.leanback.widget.GuidedActionEditText#onTouchEvent(android.view.MotionEvent) parameter #0:
Invalid nullability on parameter `event` in method `onTouchEvent`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.leanback.widget.ShadowOverlayContainer#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
KotlinOperator: androidx.leanback.widget.ObjectAdapter#get(int):
@@ -1135,6 +1133,8 @@
Missing nullability on field `TOP_FRACTION` in class `class androidx.leanback.graphics.CompositeDrawable.ChildDrawable`
MissingNullability: androidx.leanback.graphics.FitWidthBitmapDrawable#PROPERTY_VERTICAL_OFFSET:
Missing nullability on field `PROPERTY_VERTICAL_OFFSET` in class `class androidx.leanback.graphics.FitWidthBitmapDrawable`
+MissingNullability: androidx.leanback.graphics.FitWidthBitmapDrawable#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.leanback.graphics.FitWidthBitmapDrawable#getBitmap():
Missing nullability on method `getBitmap` return
MissingNullability: androidx.leanback.graphics.FitWidthBitmapDrawable#getConstantState():
@@ -2189,6 +2189,8 @@
Missing nullability on parameter `context` in method `ShadowOverlayContainer`
MissingNullability: androidx.leanback.widget.ShadowOverlayContainer#ShadowOverlayContainer(android.content.Context, android.util.AttributeSet, int) parameter #1:
Missing nullability on parameter `attrs` in method `ShadowOverlayContainer`
+MissingNullability: androidx.leanback.widget.ShadowOverlayContainer#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.leanback.widget.ShadowOverlayContainer#getWrappedView():
Missing nullability on method `getWrappedView` return
MissingNullability: androidx.leanback.widget.ShadowOverlayContainer#prepareParentForShadow(android.view.ViewGroup) parameter #0:
diff --git a/libraryversions.toml b/libraryversions.toml
index 0cbd30c..5b5d998 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -29,7 +29,7 @@
CONSTRAINTLAYOUT_CORE = "1.1.0-alpha10"
CONTENTPAGER = "1.1.0-alpha01"
COORDINATORLAYOUT = "1.3.0-alpha01"
-CORE = "1.11.0-beta01"
+CORE = "1.12.0-beta01"
CORE_ANIMATION = "1.0.0-beta02"
CORE_ANIMATION_TESTING = "1.0.0-beta01"
CORE_APPDIGEST = "1.0.0-alpha01"
@@ -40,8 +40,9 @@
CORE_REMOTEVIEWS = "1.0.0-beta05"
CORE_ROLE = "1.2.0-alpha01"
CORE_SPLASHSCREEN = "1.1.0-alpha01"
+CORE_TELECOM = "1.0.0-alpha01"
CORE_UWB = "1.0.0-alpha06"
-CREDENTIALS = "1.0.0-alpha09"
+CREDENTIALS = "1.2.0-alpha05"
CURSORADAPTER = "1.1.0-alpha01"
CUSTOMVIEW = "1.2.0-alpha03"
CUSTOMVIEW_POOLINGCONTAINER = "1.1.0-alpha01"
@@ -62,10 +63,11 @@
GLANCE_TEMPLATE = "1.0.0-alpha06"
GLANCE_WEAR_TILES = "1.0.0-alpha06"
GRAPHICS_CORE = "1.0.0-alpha04"
+GRAPHICS_PATH = "1.0.0-alpha02"
GRAPHICS_FILTERS = "1.0.0-alpha01"
GRAPHICS_SHAPES = "1.0.0-alpha03"
GRIDLAYOUT = "1.1.0-beta01"
-HEALTH_CONNECT = "1.0.0-alpha11"
+HEALTH_CONNECT = "1.1.0-alpha01"
HEALTH_SERVICES_CLIENT = "1.0.0-beta04"
HEIFWRITER = "1.1.0-alpha02"
HILT = "1.1.0-alpha03"
@@ -86,7 +88,7 @@
LOADER = "1.2.0-alpha01"
MEDIA = "1.7.0-alpha02"
MEDIA2 = "1.3.0-alpha01"
-MEDIAROUTER = "1.5.0-alpha01"
+MEDIAROUTER = "1.6.0-alpha04"
METRICS = "1.0.0-alpha05"
NAVIGATION = "2.7.0-alpha01"
PAGING = "3.2.0-alpha06"
diff --git a/lifecycle/lifecycle-common-java8/api/2.6.0-beta02.txt b/lifecycle/lifecycle-common-java8/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-common-java8/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/lifecycle/lifecycle-common-java8/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-common-java8/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-common-java8/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/lifecycle/lifecycle-common-java8/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-common-java8/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-common-java8/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/lifecycle/lifecycle-common/api/2.6.0-beta02.txt b/lifecycle/lifecycle-common/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..f3dc4c9
--- /dev/null
+++ b/lifecycle/lifecycle-common/api/2.6.0-beta02.txt
@@ -0,0 +1,99 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
+ method public default void onCreate(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onDestroy(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onPause(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onResume(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStart(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStop(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ public abstract class Lifecycle {
+ ctor public Lifecycle();
+ method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+ method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+ }
+
+ public enum Lifecycle.Event {
+ method public static final androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public final androidx.lifecycle.Lifecycle.State getTargetState();
+ method public static final androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.Event valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.Event[] values();
+ property public final androidx.lifecycle.Lifecycle.State targetState;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_ANY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_CREATE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_DESTROY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_PAUSE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_RESUME;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_START;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_STOP;
+ field public static final androidx.lifecycle.Lifecycle.Event.Companion Companion;
+ }
+
+ public static final class Lifecycle.Event.Companion {
+ method public androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ }
+
+ public enum Lifecycle.State {
+ method public final boolean isAtLeast(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.State valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.State[] values();
+ enum_constant public static final androidx.lifecycle.Lifecycle.State CREATED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State DESTROYED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State INITIALIZED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State RESUMED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State STARTED;
+ }
+
+ public abstract class LifecycleCoroutineScope implements kotlinx.coroutines.CoroutineScope {
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenCreated(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenResumed(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenStarted(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public fun interface LifecycleEventObserver extends androidx.lifecycle.LifecycleObserver {
+ method public void onStateChanged(androidx.lifecycle.LifecycleOwner source, androidx.lifecycle.Lifecycle.Event event);
+ }
+
+ public final class LifecycleKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getCoroutineScope(androidx.lifecycle.Lifecycle);
+ }
+
+ public interface LifecycleObserver {
+ }
+
+ public interface LifecycleOwner {
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public abstract androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public final class LifecycleOwnerKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getLifecycleScope(androidx.lifecycle.LifecycleOwner);
+ }
+
+ @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface OnLifecycleEvent {
+ method @Deprecated public abstract androidx.lifecycle.Lifecycle.Event! value();
+ }
+
+ public final class PausingDispatcherKt {
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State minState, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-common/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-common/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..f3dc4c9
--- /dev/null
+++ b/lifecycle/lifecycle-common/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,99 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
+ method public default void onCreate(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onDestroy(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onPause(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onResume(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStart(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStop(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ public abstract class Lifecycle {
+ ctor public Lifecycle();
+ method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+ method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+ }
+
+ public enum Lifecycle.Event {
+ method public static final androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public final androidx.lifecycle.Lifecycle.State getTargetState();
+ method public static final androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.Event valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.Event[] values();
+ property public final androidx.lifecycle.Lifecycle.State targetState;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_ANY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_CREATE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_DESTROY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_PAUSE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_RESUME;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_START;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_STOP;
+ field public static final androidx.lifecycle.Lifecycle.Event.Companion Companion;
+ }
+
+ public static final class Lifecycle.Event.Companion {
+ method public androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ }
+
+ public enum Lifecycle.State {
+ method public final boolean isAtLeast(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.State valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.State[] values();
+ enum_constant public static final androidx.lifecycle.Lifecycle.State CREATED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State DESTROYED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State INITIALIZED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State RESUMED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State STARTED;
+ }
+
+ public abstract class LifecycleCoroutineScope implements kotlinx.coroutines.CoroutineScope {
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenCreated(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenResumed(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenStarted(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public fun interface LifecycleEventObserver extends androidx.lifecycle.LifecycleObserver {
+ method public void onStateChanged(androidx.lifecycle.LifecycleOwner source, androidx.lifecycle.Lifecycle.Event event);
+ }
+
+ public final class LifecycleKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getCoroutineScope(androidx.lifecycle.Lifecycle);
+ }
+
+ public interface LifecycleObserver {
+ }
+
+ public interface LifecycleOwner {
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public abstract androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public final class LifecycleOwnerKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getLifecycleScope(androidx.lifecycle.LifecycleOwner);
+ }
+
+ @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface OnLifecycleEvent {
+ method @Deprecated public abstract androidx.lifecycle.Lifecycle.Event! value();
+ }
+
+ public final class PausingDispatcherKt {
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State minState, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-common/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-common/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..05b2709
--- /dev/null
+++ b/lifecycle/lifecycle-common/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,116 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
+ method public default void onCreate(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onDestroy(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onPause(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onResume(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStart(androidx.lifecycle.LifecycleOwner owner);
+ method public default void onStop(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface GeneratedAdapter {
+ method public void callMethods(androidx.lifecycle.LifecycleOwner source, androidx.lifecycle.Lifecycle.Event event, boolean onAny, androidx.lifecycle.MethodCallsLogger? logger);
+ }
+
+ @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface GenericLifecycleObserver extends androidx.lifecycle.LifecycleEventObserver {
+ }
+
+ public abstract class Lifecycle {
+ ctor public Lifecycle();
+ method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+ method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+ }
+
+ public enum Lifecycle.Event {
+ method public static final androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public final androidx.lifecycle.Lifecycle.State getTargetState();
+ method public static final androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public static final androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.Event valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.Event[] values();
+ property public final androidx.lifecycle.Lifecycle.State targetState;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_ANY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_CREATE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_DESTROY;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_PAUSE;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_RESUME;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_START;
+ enum_constant public static final androidx.lifecycle.Lifecycle.Event ON_STOP;
+ field public static final androidx.lifecycle.Lifecycle.Event.Companion Companion;
+ }
+
+ public static final class Lifecycle.Event.Companion {
+ method public androidx.lifecycle.Lifecycle.Event? downFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? downTo(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upFrom(androidx.lifecycle.Lifecycle.State state);
+ method public androidx.lifecycle.Lifecycle.Event? upTo(androidx.lifecycle.Lifecycle.State state);
+ }
+
+ public enum Lifecycle.State {
+ method public final boolean isAtLeast(androidx.lifecycle.Lifecycle.State state);
+ method public static androidx.lifecycle.Lifecycle.State valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.lifecycle.Lifecycle.State[] values();
+ enum_constant public static final androidx.lifecycle.Lifecycle.State CREATED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State DESTROYED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State INITIALIZED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State RESUMED;
+ enum_constant public static final androidx.lifecycle.Lifecycle.State STARTED;
+ }
+
+ public abstract class LifecycleCoroutineScope implements kotlinx.coroutines.CoroutineScope {
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenCreated(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenResumed(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @Deprecated public final kotlinx.coroutines.Job launchWhenStarted(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public fun interface LifecycleEventObserver extends androidx.lifecycle.LifecycleObserver {
+ method public void onStateChanged(androidx.lifecycle.LifecycleOwner source, androidx.lifecycle.Lifecycle.Event event);
+ }
+
+ public final class LifecycleKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getCoroutineScope(androidx.lifecycle.Lifecycle);
+ }
+
+ public interface LifecycleObserver {
+ }
+
+ public interface LifecycleOwner {
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public abstract androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public final class LifecycleOwnerKt {
+ method public static androidx.lifecycle.LifecycleCoroutineScope getLifecycleScope(androidx.lifecycle.LifecycleOwner);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class Lifecycling {
+ method public static String getAdapterName(String className);
+ method public static androidx.lifecycle.LifecycleEventObserver lifecycleEventObserver(Object object);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MethodCallsLogger {
+ ctor public MethodCallsLogger();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean approveCall(String name, int type);
+ }
+
+ @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface OnLifecycleEvent {
+ method @Deprecated public abstract androidx.lifecycle.Lifecycle.Event! value();
+ }
+
+ public final class PausingDispatcherKt {
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ method @Deprecated public static suspend <T> Object? whenStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State minState, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block, kotlin.coroutines.Continuation<? super T>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-extensions/api/2.6.0-beta02.txt b/lifecycle/lifecycle-extensions/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..88798d8
--- /dev/null
+++ b/lifecycle/lifecycle-extensions/api/2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ @Deprecated public class ViewModelProviders {
+ ctor @Deprecated public ViewModelProviders();
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ }
+
+ @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
+ ctor @Deprecated public ViewModelProviders.DefaultFactory(android.app.Application);
+ }
+
+ @Deprecated public class ViewModelStores {
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-extensions/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-extensions/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..88798d8
--- /dev/null
+++ b/lifecycle/lifecycle-extensions/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ @Deprecated public class ViewModelProviders {
+ ctor @Deprecated public ViewModelProviders();
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ }
+
+ @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
+ ctor @Deprecated public ViewModelProviders.DefaultFactory(android.app.Application);
+ }
+
+ @Deprecated public class ViewModelStores {
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-extensions/api/res-2.6.0-beta02.txt
similarity index 100%
rename from webkit/webkit/api/res-1.6.0-beta02.txt
rename to lifecycle/lifecycle-extensions/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-extensions/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-extensions/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..88798d8
--- /dev/null
+++ b/lifecycle/lifecycle-extensions/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ @Deprecated public class ViewModelProviders {
+ ctor @Deprecated public ViewModelProviders();
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+ }
+
+ @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
+ ctor @Deprecated public ViewModelProviders.DefaultFactory(android.app.Application);
+ }
+
+ @Deprecated public class ViewModelStores {
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
+ method @Deprecated @MainThread public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-core-ktx/api/2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..daac648
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataKt {
+ method @Deprecated @MainThread public static inline <T> androidx.lifecycle.Observer<T> observe(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner owner, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onChanged);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-core-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..daac648
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataKt {
+ method @Deprecated @MainThread public static inline <T> androidx.lifecycle.Observer<T> observe(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner owner, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onChanged);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core-ktx/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-livedata-core-ktx/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-livedata-core-ktx/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..daac648
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataKt {
+ method @Deprecated @MainThread public static inline <T> androidx.lifecycle.Observer<T> observe(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner owner, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onChanged);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-core/api/2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..f528b4e
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core/api/2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class LiveData<T> {
+ ctor public LiveData(T!);
+ ctor public LiveData();
+ method public T? getValue();
+ method public boolean hasActiveObservers();
+ method public boolean hasObservers();
+ method public boolean isInitialized();
+ method @MainThread public void observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void observeForever(androidx.lifecycle.Observer<? super T>);
+ method protected void onActive();
+ method protected void onInactive();
+ method protected void postValue(T!);
+ method @MainThread public void removeObserver(androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void removeObservers(androidx.lifecycle.LifecycleOwner);
+ method @MainThread protected void setValue(T!);
+ }
+
+ public class MutableLiveData<T> extends androidx.lifecycle.LiveData<T> {
+ ctor public MutableLiveData(T!);
+ ctor public MutableLiveData();
+ method public void postValue(T!);
+ method public void setValue(T!);
+ }
+
+ public fun interface Observer<T> {
+ method public void onChanged(T? value);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-core/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..f528b4e
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class LiveData<T> {
+ ctor public LiveData(T!);
+ ctor public LiveData();
+ method public T? getValue();
+ method public boolean hasActiveObservers();
+ method public boolean hasObservers();
+ method public boolean isInitialized();
+ method @MainThread public void observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void observeForever(androidx.lifecycle.Observer<? super T>);
+ method protected void onActive();
+ method protected void onInactive();
+ method protected void postValue(T!);
+ method @MainThread public void removeObserver(androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void removeObservers(androidx.lifecycle.LifecycleOwner);
+ method @MainThread protected void setValue(T!);
+ }
+
+ public class MutableLiveData<T> extends androidx.lifecycle.LiveData<T> {
+ ctor public MutableLiveData(T!);
+ ctor public MutableLiveData();
+ method public void postValue(T!);
+ method public void setValue(T!);
+ }
+
+ public fun interface Observer<T> {
+ method public void onChanged(T? value);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-livedata-core/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-livedata-core/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-core/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..f528b4e
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class LiveData<T> {
+ ctor public LiveData(T!);
+ ctor public LiveData();
+ method public T? getValue();
+ method public boolean hasActiveObservers();
+ method public boolean hasObservers();
+ method public boolean isInitialized();
+ method @MainThread public void observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void observeForever(androidx.lifecycle.Observer<? super T>);
+ method protected void onActive();
+ method protected void onInactive();
+ method protected void postValue(T!);
+ method @MainThread public void removeObserver(androidx.lifecycle.Observer<? super T>);
+ method @MainThread public void removeObservers(androidx.lifecycle.LifecycleOwner);
+ method @MainThread protected void setValue(T!);
+ }
+
+ public class MutableLiveData<T> extends androidx.lifecycle.LiveData<T> {
+ ctor public MutableLiveData(T!);
+ ctor public MutableLiveData();
+ method public void postValue(T!);
+ method public void setValue(T!);
+ }
+
+ public fun interface Observer<T> {
+ method public void onChanged(T? value);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-ktx/api/2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..bae0928
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1,25 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class CoroutineLiveDataKt {
+ method public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public final class FlowLiveDataConversions {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout);
+ }
+
+ public interface LiveDataScope<T> {
+ method public suspend Object? emit(T? value, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? emitSource(androidx.lifecycle.LiveData<T> source, kotlin.coroutines.Continuation<? super kotlinx.coroutines.DisposableHandle>);
+ method public T? getLatestValue();
+ property public abstract T? latestValue;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..bae0928
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,25 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class CoroutineLiveDataKt {
+ method public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public final class FlowLiveDataConversions {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout);
+ }
+
+ public interface LiveDataScope<T> {
+ method public suspend Object? emit(T? value, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? emitSource(androidx.lifecycle.LiveData<T> source, kotlin.coroutines.Continuation<? super kotlinx.coroutines.DisposableHandle>);
+ method public T? getLatestValue();
+ property public abstract T? latestValue;
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-livedata-ktx/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-livedata-ktx/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-livedata-ktx/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..bae0928
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,25 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class CoroutineLiveDataKt {
+ method public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> liveData(optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout, kotlin.jvm.functions.Function2<? super androidx.lifecycle.LiveDataScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
+ }
+
+ public final class FlowLiveDataConversions {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> asFlow(androidx.lifecycle.LiveData<T>);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, optional long timeoutInMs);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
+ method public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public static <T> androidx.lifecycle.LiveData<T> asLiveData(kotlinx.coroutines.flow.Flow<? extends T>, optional kotlin.coroutines.CoroutineContext context, java.time.Duration timeout);
+ }
+
+ public interface LiveDataScope<T> {
+ method public suspend Object? emit(T? value, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? emitSource(androidx.lifecycle.LiveData<T> source, kotlin.coroutines.Continuation<? super kotlinx.coroutines.DisposableHandle>);
+ method public T? getLatestValue();
+ property public abstract T? latestValue;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata/api/2.6.0-beta02.txt b/lifecycle/lifecycle-livedata/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..9b1bf6c
--- /dev/null
+++ b/lifecycle/lifecycle-livedata/api/2.6.0-beta02.txt
@@ -0,0 +1,20 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class MediatorLiveData<T> extends androidx.lifecycle.MutableLiveData<T> {
+ ctor public MediatorLiveData();
+ ctor public MediatorLiveData(T!);
+ method @MainThread public <S> void addSource(androidx.lifecycle.LiveData<S!>, androidx.lifecycle.Observer<? super S>);
+ method @MainThread public <S> void removeSource(androidx.lifecycle.LiveData<S!>);
+ }
+
+ public final class Transformations {
+ method @CheckResult @MainThread public static <X> androidx.lifecycle.LiveData<X> distinctUntilChanged(androidx.lifecycle.LiveData<X>);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,Y> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,Y> mapFunction);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,androidx.lifecycle.LiveData<Y>> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,androidx.lifecycle.LiveData<Y>> switchMapFunction);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-livedata/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..9b1bf6c
--- /dev/null
+++ b/lifecycle/lifecycle-livedata/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,20 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class MediatorLiveData<T> extends androidx.lifecycle.MutableLiveData<T> {
+ ctor public MediatorLiveData();
+ ctor public MediatorLiveData(T!);
+ method @MainThread public <S> void addSource(androidx.lifecycle.LiveData<S!>, androidx.lifecycle.Observer<? super S>);
+ method @MainThread public <S> void removeSource(androidx.lifecycle.LiveData<S!>);
+ }
+
+ public final class Transformations {
+ method @CheckResult @MainThread public static <X> androidx.lifecycle.LiveData<X> distinctUntilChanged(androidx.lifecycle.LiveData<X>);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,Y> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,Y> mapFunction);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,androidx.lifecycle.LiveData<Y>> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,androidx.lifecycle.LiveData<Y>> switchMapFunction);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-livedata/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-livedata/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-livedata/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-livedata/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..bb61b39
--- /dev/null
+++ b/lifecycle/lifecycle-livedata/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,29 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ComputableLiveData<T> {
+ ctor public ComputableLiveData(optional java.util.concurrent.Executor executor);
+ ctor public ComputableLiveData();
+ method @WorkerThread protected abstract T! compute();
+ method public androidx.lifecycle.LiveData<T> getLiveData();
+ method public void invalidate();
+ property public androidx.lifecycle.LiveData<T> liveData;
+ }
+
+ public class MediatorLiveData<T> extends androidx.lifecycle.MutableLiveData<T> {
+ ctor public MediatorLiveData();
+ ctor public MediatorLiveData(T!);
+ method @MainThread public <S> void addSource(androidx.lifecycle.LiveData<S!>, androidx.lifecycle.Observer<? super S>);
+ method @MainThread public <S> void removeSource(androidx.lifecycle.LiveData<S!>);
+ }
+
+ public final class Transformations {
+ method @CheckResult @MainThread public static <X> androidx.lifecycle.LiveData<X> distinctUntilChanged(androidx.lifecycle.LiveData<X>);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,Y> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,Y> mapFunction);
+ method @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, kotlin.jvm.functions.Function1<X,androidx.lifecycle.LiveData<Y>> transform);
+ method @Deprecated @CheckResult @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,androidx.lifecycle.LiveData<Y>> switchMapFunction);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-process/api/2.6.0-beta02.txt b/lifecycle/lifecycle-process/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..891c9c6
--- /dev/null
+++ b/lifecycle/lifecycle-process/api/2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ProcessLifecycleInitializer implements androidx.startup.Initializer<androidx.lifecycle.LifecycleOwner> {
+ ctor public ProcessLifecycleInitializer();
+ method public androidx.lifecycle.LifecycleOwner create(android.content.Context context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+ }
+
+ public final class ProcessLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner get();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ field public static final androidx.lifecycle.ProcessLifecycleOwner.Companion Companion;
+ }
+
+ public static final class ProcessLifecycleOwner.Companion {
+ method public androidx.lifecycle.LifecycleOwner get();
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-process/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-process/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..891c9c6
--- /dev/null
+++ b/lifecycle/lifecycle-process/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ProcessLifecycleInitializer implements androidx.startup.Initializer<androidx.lifecycle.LifecycleOwner> {
+ ctor public ProcessLifecycleInitializer();
+ method public androidx.lifecycle.LifecycleOwner create(android.content.Context context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+ }
+
+ public final class ProcessLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner get();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ field public static final androidx.lifecycle.ProcessLifecycleOwner.Companion Companion;
+ }
+
+ public static final class ProcessLifecycleOwner.Companion {
+ method public androidx.lifecycle.LifecycleOwner get();
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-process/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-process/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-process/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-process/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..891c9c6
--- /dev/null
+++ b/lifecycle/lifecycle-process/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ProcessLifecycleInitializer implements androidx.startup.Initializer<androidx.lifecycle.LifecycleOwner> {
+ ctor public ProcessLifecycleInitializer();
+ method public androidx.lifecycle.LifecycleOwner create(android.content.Context context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+ }
+
+ public final class ProcessLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner get();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ field public static final androidx.lifecycle.ProcessLifecycleOwner.Companion Companion;
+ }
+
+ public static final class ProcessLifecycleOwner.Companion {
+ method public androidx.lifecycle.LifecycleOwner get();
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/api/2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams-ktx/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-reactivestreams-ktx/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/lifecycle/lifecycle-reactivestreams/api/2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..138dd3e
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams/api/2.6.0-beta02.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataReactiveStreams {
+ method public static <T> androidx.lifecycle.LiveData<T> fromPublisher(org.reactivestreams.Publisher<T>);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LifecycleOwner lifecycle, androidx.lifecycle.LiveData<T> liveData);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner lifecycle);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-reactivestreams/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..138dd3e
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataReactiveStreams {
+ method public static <T> androidx.lifecycle.LiveData<T> fromPublisher(org.reactivestreams.Publisher<T>);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LifecycleOwner lifecycle, androidx.lifecycle.LiveData<T> liveData);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner lifecycle);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-reactivestreams/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-reactivestreams/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-reactivestreams/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..138dd3e
--- /dev/null
+++ b/lifecycle/lifecycle-reactivestreams/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class LiveDataReactiveStreams {
+ method public static <T> androidx.lifecycle.LiveData<T> fromPublisher(org.reactivestreams.Publisher<T>);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LifecycleOwner lifecycle, androidx.lifecycle.LiveData<T> liveData);
+ method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LiveData<T>, androidx.lifecycle.LifecycleOwner lifecycle);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-compose/api/2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-compose/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..c80fa83
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/api/2.6.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.lifecycle.compose {
+
+ public final class FlowExtKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..c80fa83
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.lifecycle.compose {
+
+ public final class FlowExtKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-runtime-compose/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-runtime-compose/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-runtime-compose/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-compose/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..c80fa83
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.lifecycle.compose {
+
+ public final class FlowExtKt {
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.StateFlow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, optional androidx.lifecycle.LifecycleOwner lifecycleOwner, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-ktx/api/2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..2ee0d85
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class FlowExtKt {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> flowWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState);
+ }
+
+ public final class LifecycleDestroyedException extends java.util.concurrent.CancellationException {
+ ctor public LifecycleDestroyedException();
+ }
+
+ public final class RepeatOnLifecycleKt {
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class ViewKt {
+ method @Deprecated public static androidx.lifecycle.LifecycleOwner? findViewTreeLifecycleOwner(android.view.View);
+ }
+
+ public final class WithLifecycleStateKt {
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..2ee0d85
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class FlowExtKt {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> flowWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState);
+ }
+
+ public final class LifecycleDestroyedException extends java.util.concurrent.CancellationException {
+ ctor public LifecycleDestroyedException();
+ }
+
+ public final class RepeatOnLifecycleKt {
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class ViewKt {
+ method @Deprecated public static androidx.lifecycle.LifecycleOwner? findViewTreeLifecycleOwner(android.view.View);
+ }
+
+ public final class WithLifecycleStateKt {
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-runtime-ktx/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-runtime-ktx/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-runtime-ktx/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..a998f6e
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,35 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class FlowExtKt {
+ method public static <T> kotlinx.coroutines.flow.Flow<T> flowWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState);
+ }
+
+ public final class LifecycleDestroyedException extends java.util.concurrent.CancellationException {
+ ctor public LifecycleDestroyedException();
+ }
+
+ public final class RepeatOnLifecycleKt {
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? repeatOnLifecycle(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class ViewKt {
+ method @Deprecated public static androidx.lifecycle.LifecycleOwner? findViewTreeLifecycleOwner(android.view.View);
+ }
+
+ public final class WithLifecycleStateKt {
+ method @kotlin.PublishedApi internal static suspend <R> Object? suspendWithStateAtLeastUnchecked(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, boolean dispatchNeeded, kotlinx.coroutines.CoroutineDispatcher lifecycleDispatcher, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withCreated(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withResumed(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.Lifecycle, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStarted(androidx.lifecycle.LifecycleOwner, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method public static suspend inline <R> Object? withStateAtLeast(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ method @kotlin.PublishedApi internal static suspend inline <R> Object? withStateAtLeastUnchecked(androidx.lifecycle.Lifecycle, androidx.lifecycle.Lifecycle.State state, kotlin.jvm.functions.Function0<? extends R> block, kotlin.coroutines.Continuation<? super R>);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-testing/api/2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-testing/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..47a819e
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/api/2.6.0-beta02.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.lifecycle.testing {
+
+ public final class TestLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState);
+ ctor public TestLifecycleOwner();
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public final androidx.lifecycle.Lifecycle.State currentState;
+ property public androidx.lifecycle.LifecycleRegistry lifecycle;
+ property public final int observerCount;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime-testing/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-testing/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..47a819e
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.lifecycle.testing {
+
+ public final class TestLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState);
+ ctor public TestLifecycleOwner();
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public final androidx.lifecycle.Lifecycle.State currentState;
+ property public androidx.lifecycle.LifecycleRegistry lifecycle;
+ property public final int observerCount;
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-runtime-testing/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-runtime-testing/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-runtime-testing/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime-testing/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..47a819e
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.lifecycle.testing {
+
+ public final class TestLifecycleOwner implements androidx.lifecycle.LifecycleOwner {
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestLifecycleOwner(optional androidx.lifecycle.Lifecycle.State initialState);
+ ctor public TestLifecycleOwner();
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public final androidx.lifecycle.Lifecycle.State currentState;
+ property public androidx.lifecycle.LifecycleRegistry lifecycle;
+ property public final int observerCount;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime/api/2.6.0-beta02.txt b/lifecycle/lifecycle-runtime/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e72bd60
--- /dev/null
+++ b/lifecycle/lifecycle-runtime/api/2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleRegistry extends androidx.lifecycle.Lifecycle {
+ ctor public LifecycleRegistry(androidx.lifecycle.LifecycleOwner provider);
+ method public void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @VisibleForTesting public static final androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method @Deprecated @MainThread public void markState(androidx.lifecycle.Lifecycle.State state);
+ method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public androidx.lifecycle.Lifecycle.State currentState;
+ property public int observerCount;
+ field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
+ }
+
+ public static final class LifecycleRegistry.Companion {
+ method @VisibleForTesting public androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ @Deprecated public interface LifecycleRegistryOwner extends androidx.lifecycle.LifecycleOwner {
+ method @Deprecated public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ }
+
+ public final class ViewTreeLifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.LifecycleOwner? lifecycleOwner);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-runtime/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e72bd60
--- /dev/null
+++ b/lifecycle/lifecycle-runtime/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,33 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleRegistry extends androidx.lifecycle.Lifecycle {
+ ctor public LifecycleRegistry(androidx.lifecycle.LifecycleOwner provider);
+ method public void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @VisibleForTesting public static final androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method @Deprecated @MainThread public void markState(androidx.lifecycle.Lifecycle.State state);
+ method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public androidx.lifecycle.Lifecycle.State currentState;
+ property public int observerCount;
+ field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
+ }
+
+ public static final class LifecycleRegistry.Companion {
+ method @VisibleForTesting public androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ @Deprecated public interface LifecycleRegistryOwner extends androidx.lifecycle.LifecycleOwner {
+ method @Deprecated public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ }
+
+ public final class ViewTreeLifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.LifecycleOwner? lifecycleOwner);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-runtime/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-runtime/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-runtime/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-runtime/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..704cdb4
--- /dev/null
+++ b/lifecycle/lifecycle-runtime/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,58 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleRegistry extends androidx.lifecycle.Lifecycle {
+ ctor public LifecycleRegistry(androidx.lifecycle.LifecycleOwner provider);
+ method public void addObserver(androidx.lifecycle.LifecycleObserver observer);
+ method @VisibleForTesting public static final androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ method public androidx.lifecycle.Lifecycle.State getCurrentState();
+ method public int getObserverCount();
+ method public void handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event event);
+ method @Deprecated @MainThread public void markState(androidx.lifecycle.Lifecycle.State state);
+ method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
+ method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
+ property public androidx.lifecycle.Lifecycle.State currentState;
+ property public int observerCount;
+ field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
+ }
+
+ public static final class LifecycleRegistry.Companion {
+ method @VisibleForTesting public androidx.lifecycle.LifecycleRegistry createUnsafe(androidx.lifecycle.LifecycleOwner owner);
+ }
+
+ @Deprecated public interface LifecycleRegistryOwner extends androidx.lifecycle.LifecycleOwner {
+ method @Deprecated public androidx.lifecycle.LifecycleRegistry getLifecycle();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ReportFragment extends android.app.Fragment {
+ ctor public ReportFragment();
+ method public static final androidx.lifecycle.ReportFragment get(android.app.Activity);
+ method public static final void injectIfNeededIn(android.app.Activity activity);
+ method public void onActivityCreated(android.os.Bundle? savedInstanceState);
+ method public void onDestroy();
+ method public void onPause();
+ method public void onResume();
+ method public void onStart();
+ method public void onStop();
+ method public final void setProcessListener(androidx.lifecycle.ReportFragment.ActivityInitializationListener? processListener);
+ field public static final androidx.lifecycle.ReportFragment.Companion Companion;
+ }
+
+ public static interface ReportFragment.ActivityInitializationListener {
+ method public void onCreate();
+ method public void onResume();
+ method public void onStart();
+ }
+
+ public static final class ReportFragment.Companion {
+ method public androidx.lifecycle.ReportFragment get(android.app.Activity);
+ method public void injectIfNeededIn(android.app.Activity activity);
+ }
+
+ public final class ViewTreeLifecycleOwner {
+ method public static androidx.lifecycle.LifecycleOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.LifecycleOwner? lifecycleOwner);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-service/api/2.6.0-beta02.txt b/lifecycle/lifecycle-service/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..bebcd93
--- /dev/null
+++ b/lifecycle/lifecycle-service/api/2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+ ctor public LifecycleService();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @CallSuper public android.os.IBinder? onBind(android.content.Intent intent);
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public class ServiceLifecycleDispatcher {
+ ctor public ServiceLifecycleDispatcher(androidx.lifecycle.LifecycleOwner provider);
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public void onServicePreSuperOnBind();
+ method public void onServicePreSuperOnCreate();
+ method public void onServicePreSuperOnDestroy();
+ method public void onServicePreSuperOnStart();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-service/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-service/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..bebcd93
--- /dev/null
+++ b/lifecycle/lifecycle-service/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+ ctor public LifecycleService();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @CallSuper public android.os.IBinder? onBind(android.content.Intent intent);
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public class ServiceLifecycleDispatcher {
+ ctor public ServiceLifecycleDispatcher(androidx.lifecycle.LifecycleOwner provider);
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public void onServicePreSuperOnBind();
+ method public void onServicePreSuperOnCreate();
+ method public void onServicePreSuperOnDestroy();
+ method public void onServicePreSuperOnStart();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-service/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-service/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-service/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-service/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..bebcd93
--- /dev/null
+++ b/lifecycle/lifecycle-service/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class LifecycleService extends android.app.Service implements androidx.lifecycle.LifecycleOwner {
+ ctor public LifecycleService();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @CallSuper public android.os.IBinder? onBind(android.content.Intent intent);
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+ public class ServiceLifecycleDispatcher {
+ ctor public ServiceLifecycleDispatcher(androidx.lifecycle.LifecycleOwner provider);
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public void onServicePreSuperOnBind();
+ method public void onServicePreSuperOnCreate();
+ method public void onServicePreSuperOnDestroy();
+ method public void onServicePreSuperOnStart();
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-compose/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..05b6910
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/api/2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.lifecycle.viewmodel.compose {
+
+ public final class LocalViewModelStoreOwner {
+ method @androidx.compose.runtime.Composable public androidx.lifecycle.ViewModelStoreOwner? getCurrent();
+ method public infix androidx.compose.runtime.ProvidedValue<androidx.lifecycle.ViewModelStoreOwner> provides(androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner);
+ property @androidx.compose.runtime.Composable public final androidx.lifecycle.ViewModelStoreOwner? current;
+ field public static final androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner INSTANCE;
+ }
+
+ public final class SavedStateHandleSaverKt {
+ }
+
+ public final class ViewModelKt {
+ method @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/current.ignore b/lifecycle/lifecycle-viewmodel-compose/api/current.ignore
new file mode 100644
index 0000000..0a5b8ff
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.lifecycle.viewmodel.compose.SavedStateHandleSaverKt:
+ Removed class androidx.lifecycle.viewmodel.compose.SavedStateHandleSaverKt
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..188b922
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,30 @@
+// Signature format: 4.0
+package androidx.lifecycle.viewmodel.compose {
+
+ public final class LocalViewModelStoreOwner {
+ method @androidx.compose.runtime.Composable public androidx.lifecycle.ViewModelStoreOwner? getCurrent();
+ method public infix androidx.compose.runtime.ProvidedValue<androidx.lifecycle.ViewModelStoreOwner> provides(androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner);
+ property @androidx.compose.runtime.Composable public final androidx.lifecycle.ViewModelStoreOwner? current;
+ field public static final androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner INSTANCE;
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface SavedStateHandleSaveableApi {
+ }
+
+ public final class SavedStateHandleSaverKt {
+ method @androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi public static <T> T saveable(androidx.lifecycle.SavedStateHandle, String key, optional androidx.compose.runtime.saveable.Saver<T,?> saver, kotlin.jvm.functions.Function0<? extends T> init);
+ method @androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi public static <T> androidx.compose.runtime.MutableState<T> saveable(androidx.lifecycle.SavedStateHandle, String key, androidx.compose.runtime.saveable.Saver<T,?> stateSaver, kotlin.jvm.functions.Function0<? extends androidx.compose.runtime.MutableState<T>> init);
+ method @androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi public static <T> kotlin.properties.PropertyDelegateProvider<java.lang.Object,kotlin.properties.ReadOnlyProperty<java.lang.Object,T>> saveable(androidx.lifecycle.SavedStateHandle, optional androidx.compose.runtime.saveable.Saver<T,?> saver, kotlin.jvm.functions.Function0<? extends T> init);
+ method @androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi public static <T, M extends androidx.compose.runtime.MutableState<T>> kotlin.properties.PropertyDelegateProvider<java.lang.Object,kotlin.properties.ReadWriteProperty<java.lang.Object,T>> saveableMutableState(androidx.lifecycle.SavedStateHandle, optional androidx.compose.runtime.saveable.Saver<T,?> stateSaver, kotlin.jvm.functions.Function0<? extends M> init);
+ }
+
+ public final class ViewModelKt {
+ method @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-compose/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-viewmodel-compose/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-compose/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..05b6910
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.lifecycle.viewmodel.compose {
+
+ public final class LocalViewModelStoreOwner {
+ method @androidx.compose.runtime.Composable public androidx.lifecycle.ViewModelStoreOwner? getCurrent();
+ method public infix androidx.compose.runtime.ProvidedValue<androidx.lifecycle.ViewModelStoreOwner> provides(androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner);
+ property @androidx.compose.runtime.Composable public final androidx.lifecycle.ViewModelStoreOwner? current;
+ field public static final androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner INSTANCE;
+ }
+
+ public final class SavedStateHandleSaverKt {
+ }
+
+ public final class ViewModelKt {
+ method @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory, optional androidx.lifecycle.viewmodel.CreationExtras extras);
+ method @Deprecated @androidx.compose.runtime.Composable public static <VM extends androidx.lifecycle.ViewModel> VM viewModel(Class<VM> modelClass, optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, optional androidx.lifecycle.ViewModelProvider.Factory? factory);
+ method @androidx.compose.runtime.Composable public static inline <reified VM extends androidx.lifecycle.ViewModel> VM viewModel(optional androidx.lifecycle.ViewModelStoreOwner viewModelStoreOwner, optional String? key, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.ignore b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.ignore
new file mode 100644
index 0000000..0a5b8ff
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.lifecycle.viewmodel.compose.SavedStateHandleSaverKt:
+ Removed class androidx.lifecycle.viewmodel.compose.SavedStateHandleSaverKt
diff --git a/lifecycle/lifecycle-viewmodel-ktx/api/2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..1d1d247
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ViewModelKt {
+ method public static kotlinx.coroutines.CoroutineScope getViewModelScope(androidx.lifecycle.ViewModel);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..1d1d247
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ViewModelKt {
+ method public static kotlinx.coroutines.CoroutineScope getViewModelScope(androidx.lifecycle.ViewModel);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-ktx/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-viewmodel-ktx/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-viewmodel-ktx/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..1d1d247
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public final class ViewModelKt {
+ method public static kotlinx.coroutines.CoroutineScope getViewModelScope(androidx.lifecycle.ViewModel);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..c030c8a
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/2.6.0-beta02.txt
@@ -0,0 +1,45 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public AbstractSavedStateViewModelFactory();
+ ctor public AbstractSavedStateViewModelFactory(androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method protected abstract <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass, androidx.lifecycle.SavedStateHandle handle);
+ }
+
+ public final class SavedStateHandle {
+ ctor public SavedStateHandle(java.util.Map<java.lang.String,?> initialState);
+ ctor public SavedStateHandle();
+ method @MainThread public void clearSavedStateProvider(String key);
+ method @MainThread public operator boolean contains(String key);
+ method @MainThread public operator <T> T? get(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key, T? initialValue);
+ method @MainThread public <T> kotlinx.coroutines.flow.StateFlow<T> getStateFlow(String key, T? initialValue);
+ method @MainThread public java.util.Set<java.lang.String> keys();
+ method @MainThread public <T> T? remove(String key);
+ method @MainThread public operator <T> void set(String key, T? value);
+ method @MainThread public void setSavedStateProvider(String key, androidx.savedstate.SavedStateRegistry.SavedStateProvider provider);
+ field public static final androidx.lifecycle.SavedStateHandle.Companion Companion;
+ }
+
+ public static final class SavedStateHandle.Companion {
+ }
+
+ public final class SavedStateHandleSupport {
+ method @MainThread public static androidx.lifecycle.SavedStateHandle createSavedStateHandle(androidx.lifecycle.viewmodel.CreationExtras);
+ method @MainThread public static <T extends androidx.savedstate.SavedStateRegistryOwner & androidx.lifecycle.ViewModelStoreOwner> void enableSavedStateHandles(T);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.os.Bundle> DEFAULT_ARGS_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.savedstate.SavedStateRegistryOwner> SAVED_STATE_REGISTRY_OWNER_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.lifecycle.ViewModelStoreOwner> VIEW_MODEL_STORE_OWNER_KEY;
+ }
+
+ public final class SavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public SavedStateViewModelFactory();
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner);
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method public <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..c030c8a
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,45 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public AbstractSavedStateViewModelFactory();
+ ctor public AbstractSavedStateViewModelFactory(androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method protected abstract <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass, androidx.lifecycle.SavedStateHandle handle);
+ }
+
+ public final class SavedStateHandle {
+ ctor public SavedStateHandle(java.util.Map<java.lang.String,?> initialState);
+ ctor public SavedStateHandle();
+ method @MainThread public void clearSavedStateProvider(String key);
+ method @MainThread public operator boolean contains(String key);
+ method @MainThread public operator <T> T? get(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key, T? initialValue);
+ method @MainThread public <T> kotlinx.coroutines.flow.StateFlow<T> getStateFlow(String key, T? initialValue);
+ method @MainThread public java.util.Set<java.lang.String> keys();
+ method @MainThread public <T> T? remove(String key);
+ method @MainThread public operator <T> void set(String key, T? value);
+ method @MainThread public void setSavedStateProvider(String key, androidx.savedstate.SavedStateRegistry.SavedStateProvider provider);
+ field public static final androidx.lifecycle.SavedStateHandle.Companion Companion;
+ }
+
+ public static final class SavedStateHandle.Companion {
+ }
+
+ public final class SavedStateHandleSupport {
+ method @MainThread public static androidx.lifecycle.SavedStateHandle createSavedStateHandle(androidx.lifecycle.viewmodel.CreationExtras);
+ method @MainThread public static <T extends androidx.savedstate.SavedStateRegistryOwner & androidx.lifecycle.ViewModelStoreOwner> void enableSavedStateHandles(T);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.os.Bundle> DEFAULT_ARGS_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.savedstate.SavedStateRegistryOwner> SAVED_STATE_REGISTRY_OWNER_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.lifecycle.ViewModelStoreOwner> VIEW_MODEL_STORE_OWNER_KEY;
+ }
+
+ public final class SavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public SavedStateViewModelFactory();
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner);
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method public <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-viewmodel-savedstate/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..c030c8a
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,45 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public abstract class AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public AbstractSavedStateViewModelFactory();
+ ctor public AbstractSavedStateViewModelFactory(androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method protected abstract <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass, androidx.lifecycle.SavedStateHandle handle);
+ }
+
+ public final class SavedStateHandle {
+ ctor public SavedStateHandle(java.util.Map<java.lang.String,?> initialState);
+ ctor public SavedStateHandle();
+ method @MainThread public void clearSavedStateProvider(String key);
+ method @MainThread public operator boolean contains(String key);
+ method @MainThread public operator <T> T? get(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key);
+ method @MainThread public <T> androidx.lifecycle.MutableLiveData<T> getLiveData(String key, T? initialValue);
+ method @MainThread public <T> kotlinx.coroutines.flow.StateFlow<T> getStateFlow(String key, T? initialValue);
+ method @MainThread public java.util.Set<java.lang.String> keys();
+ method @MainThread public <T> T? remove(String key);
+ method @MainThread public operator <T> void set(String key, T? value);
+ method @MainThread public void setSavedStateProvider(String key, androidx.savedstate.SavedStateRegistry.SavedStateProvider provider);
+ field public static final androidx.lifecycle.SavedStateHandle.Companion Companion;
+ }
+
+ public static final class SavedStateHandle.Companion {
+ }
+
+ public final class SavedStateHandleSupport {
+ method @MainThread public static androidx.lifecycle.SavedStateHandle createSavedStateHandle(androidx.lifecycle.viewmodel.CreationExtras);
+ method @MainThread public static <T extends androidx.savedstate.SavedStateRegistryOwner & androidx.lifecycle.ViewModelStoreOwner> void enableSavedStateHandles(T);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.os.Bundle> DEFAULT_ARGS_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.savedstate.SavedStateRegistryOwner> SAVED_STATE_REGISTRY_OWNER_KEY;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<androidx.lifecycle.ViewModelStoreOwner> VIEW_MODEL_STORE_OWNER_KEY;
+ }
+
+ public final class SavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public SavedStateViewModelFactory();
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner);
+ ctor public SavedStateViewModelFactory(android.app.Application? application, androidx.savedstate.SavedStateRegistryOwner owner, android.os.Bundle? defaultArgs);
+ method public <T extends androidx.lifecycle.ViewModel> T create(String key, Class<T> modelClass);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel/api/2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..f8457f6
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/api/2.6.0-beta02.txt
@@ -0,0 +1,136 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class AndroidViewModel extends androidx.lifecycle.ViewModel {
+ ctor public AndroidViewModel(android.app.Application application);
+ method public <T extends android.app.Application> T getApplication();
+ }
+
+ public interface HasDefaultViewModelProviderFactory {
+ method public default androidx.lifecycle.viewmodel.CreationExtras getDefaultViewModelCreationExtras();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ property public default androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public abstract androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ }
+
+ public abstract class ViewModel {
+ ctor public ViewModel();
+ ctor public ViewModel(java.io.Closeable!...);
+ method public void addCloseable(java.io.Closeable);
+ method protected void onCleared();
+ }
+
+ public final class ViewModelLazy<VM extends androidx.lifecycle.ViewModel> implements kotlin.Lazy<VM> {
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer);
+ method public VM getValue();
+ method public boolean isInitialized();
+ property public VM value;
+ }
+
+ public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory, optional androidx.lifecycle.viewmodel.CreationExtras defaultCreationExtras);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner, androidx.lifecycle.ViewModelProvider.Factory factory);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(Class<T> modelClass);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(String key, Class<T> modelClass);
+ }
+
+ public static class ViewModelProvider.AndroidViewModelFactory extends androidx.lifecycle.ViewModelProvider.NewInstanceFactory {
+ ctor public ViewModelProvider.AndroidViewModelFactory();
+ ctor public ViewModelProvider.AndroidViewModelFactory(android.app.Application application);
+ method public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.app.Application> APPLICATION_KEY;
+ field public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.AndroidViewModelFactory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ }
+
+ public static interface ViewModelProvider.Factory {
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+ method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.Factory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ }
+
+ public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public ViewModelProvider.NewInstanceFactory();
+ field public static final androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion Companion;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<java.lang.String> VIEW_MODEL_KEY;
+ }
+
+ public static final class ViewModelProvider.NewInstanceFactory.Companion {
+ }
+
+ public final class ViewModelProviderGetKt {
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> VM get(androidx.lifecycle.ViewModelProvider);
+ }
+
+ public class ViewModelStore {
+ ctor public ViewModelStore();
+ method public final void clear();
+ }
+
+ public interface ViewModelStoreOwner {
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public abstract androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class ViewTreeViewModelKt {
+ method @Deprecated public static androidx.lifecycle.ViewModelStoreOwner? findViewTreeViewModelStoreOwner(android.view.View view);
+ }
+
+ public final class ViewTreeViewModelStoreOwner {
+ method public static androidx.lifecycle.ViewModelStoreOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.ViewModelStoreOwner? viewModelStoreOwner);
+ }
+
+}
+
+package androidx.lifecycle.viewmodel {
+
+ public abstract class CreationExtras {
+ method public abstract operator <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ }
+
+ public static final class CreationExtras.Empty extends androidx.lifecycle.viewmodel.CreationExtras {
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Empty INSTANCE;
+ }
+
+ public static interface CreationExtras.Key<T> {
+ }
+
+ @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+ ctor public InitializerViewModelFactoryBuilder();
+ method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ method public androidx.lifecycle.ViewModelProvider.Factory build();
+ }
+
+ public final class InitializerViewModelFactoryKt {
+ method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+ }
+
+ public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
+ ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
+ }
+
+ @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+ }
+
+ public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+ ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..f8457f6
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,136 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class AndroidViewModel extends androidx.lifecycle.ViewModel {
+ ctor public AndroidViewModel(android.app.Application application);
+ method public <T extends android.app.Application> T getApplication();
+ }
+
+ public interface HasDefaultViewModelProviderFactory {
+ method public default androidx.lifecycle.viewmodel.CreationExtras getDefaultViewModelCreationExtras();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ property public default androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public abstract androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ }
+
+ public abstract class ViewModel {
+ ctor public ViewModel();
+ ctor public ViewModel(java.io.Closeable!...);
+ method public void addCloseable(java.io.Closeable);
+ method protected void onCleared();
+ }
+
+ public final class ViewModelLazy<VM extends androidx.lifecycle.ViewModel> implements kotlin.Lazy<VM> {
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer);
+ method public VM getValue();
+ method public boolean isInitialized();
+ property public VM value;
+ }
+
+ public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory, optional androidx.lifecycle.viewmodel.CreationExtras defaultCreationExtras);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner, androidx.lifecycle.ViewModelProvider.Factory factory);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(Class<T> modelClass);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(String key, Class<T> modelClass);
+ }
+
+ public static class ViewModelProvider.AndroidViewModelFactory extends androidx.lifecycle.ViewModelProvider.NewInstanceFactory {
+ ctor public ViewModelProvider.AndroidViewModelFactory();
+ ctor public ViewModelProvider.AndroidViewModelFactory(android.app.Application application);
+ method public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.app.Application> APPLICATION_KEY;
+ field public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.AndroidViewModelFactory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ }
+
+ public static interface ViewModelProvider.Factory {
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+ method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.Factory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ }
+
+ public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public ViewModelProvider.NewInstanceFactory();
+ field public static final androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion Companion;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<java.lang.String> VIEW_MODEL_KEY;
+ }
+
+ public static final class ViewModelProvider.NewInstanceFactory.Companion {
+ }
+
+ public final class ViewModelProviderGetKt {
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> VM get(androidx.lifecycle.ViewModelProvider);
+ }
+
+ public class ViewModelStore {
+ ctor public ViewModelStore();
+ method public final void clear();
+ }
+
+ public interface ViewModelStoreOwner {
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public abstract androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class ViewTreeViewModelKt {
+ method @Deprecated public static androidx.lifecycle.ViewModelStoreOwner? findViewTreeViewModelStoreOwner(android.view.View view);
+ }
+
+ public final class ViewTreeViewModelStoreOwner {
+ method public static androidx.lifecycle.ViewModelStoreOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.ViewModelStoreOwner? viewModelStoreOwner);
+ }
+
+}
+
+package androidx.lifecycle.viewmodel {
+
+ public abstract class CreationExtras {
+ method public abstract operator <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ }
+
+ public static final class CreationExtras.Empty extends androidx.lifecycle.viewmodel.CreationExtras {
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Empty INSTANCE;
+ }
+
+ public static interface CreationExtras.Key<T> {
+ }
+
+ @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+ ctor public InitializerViewModelFactoryBuilder();
+ method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ method public androidx.lifecycle.ViewModelProvider.Factory build();
+ }
+
+ public final class InitializerViewModelFactoryKt {
+ method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+ }
+
+ public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
+ ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
+ }
+
+ @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+ }
+
+ public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+ ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel/api/res-2.6.0-beta02.txt
similarity index 100%
copy from webkit/webkit/api/res-1.6.0-beta02.txt
copy to lifecycle/lifecycle-viewmodel/api/res-2.6.0-beta02.txt
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_2.6.0-beta02.txt b/lifecycle/lifecycle-viewmodel/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..f8457f6
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,136 @@
+// Signature format: 4.0
+package androidx.lifecycle {
+
+ public class AndroidViewModel extends androidx.lifecycle.ViewModel {
+ ctor public AndroidViewModel(android.app.Application application);
+ method public <T extends android.app.Application> T getApplication();
+ }
+
+ public interface HasDefaultViewModelProviderFactory {
+ method public default androidx.lifecycle.viewmodel.CreationExtras getDefaultViewModelCreationExtras();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ property public default androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public abstract androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ }
+
+ public abstract class ViewModel {
+ ctor public ViewModel();
+ ctor public ViewModel(java.io.Closeable!...);
+ method public void addCloseable(java.io.Closeable);
+ method protected void onCleared();
+ }
+
+ public final class ViewModelLazy<VM extends androidx.lifecycle.ViewModel> implements kotlin.Lazy<VM> {
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer);
+ ctor public ViewModelLazy(kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory> factoryProducer);
+ method public VM getValue();
+ method public boolean isInitialized();
+ property public VM value;
+ }
+
+ public class ViewModelProvider {
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory, optional androidx.lifecycle.viewmodel.CreationExtras defaultCreationExtras);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore store, androidx.lifecycle.ViewModelProvider.Factory factory);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner);
+ ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner owner, androidx.lifecycle.ViewModelProvider.Factory factory);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(Class<T> modelClass);
+ method @MainThread public operator <T extends androidx.lifecycle.ViewModel> T get(String key, Class<T> modelClass);
+ }
+
+ public static class ViewModelProvider.AndroidViewModelFactory extends androidx.lifecycle.ViewModelProvider.NewInstanceFactory {
+ ctor public ViewModelProvider.AndroidViewModelFactory();
+ ctor public ViewModelProvider.AndroidViewModelFactory(android.app.Application application);
+ method public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<android.app.Application> APPLICATION_KEY;
+ field public static final androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.AndroidViewModelFactory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory getInstance(android.app.Application application);
+ }
+
+ public static interface ViewModelProvider.Factory {
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass);
+ method public default <T extends androidx.lifecycle.ViewModel> T create(Class<T> modelClass, androidx.lifecycle.viewmodel.CreationExtras extras);
+ method public default static androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ field public static final androidx.lifecycle.ViewModelProvider.Factory.Companion Companion;
+ }
+
+ public static final class ViewModelProvider.Factory.Companion {
+ method public androidx.lifecycle.ViewModelProvider.Factory from(androidx.lifecycle.viewmodel.ViewModelInitializer<?>... initializers);
+ }
+
+ public static class ViewModelProvider.NewInstanceFactory implements androidx.lifecycle.ViewModelProvider.Factory {
+ ctor public ViewModelProvider.NewInstanceFactory();
+ field public static final androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion Companion;
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Key<java.lang.String> VIEW_MODEL_KEY;
+ }
+
+ public static final class ViewModelProvider.NewInstanceFactory.Companion {
+ }
+
+ public final class ViewModelProviderGetKt {
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> VM get(androidx.lifecycle.ViewModelProvider);
+ }
+
+ public class ViewModelStore {
+ ctor public ViewModelStore();
+ method public final void clear();
+ }
+
+ public interface ViewModelStoreOwner {
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public abstract androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class ViewTreeViewModelKt {
+ method @Deprecated public static androidx.lifecycle.ViewModelStoreOwner? findViewTreeViewModelStoreOwner(android.view.View view);
+ }
+
+ public final class ViewTreeViewModelStoreOwner {
+ method public static androidx.lifecycle.ViewModelStoreOwner? get(android.view.View);
+ method public static void set(android.view.View, androidx.lifecycle.ViewModelStoreOwner? viewModelStoreOwner);
+ }
+
+}
+
+package androidx.lifecycle.viewmodel {
+
+ public abstract class CreationExtras {
+ method public abstract operator <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ }
+
+ public static final class CreationExtras.Empty extends androidx.lifecycle.viewmodel.CreationExtras {
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ field public static final androidx.lifecycle.viewmodel.CreationExtras.Empty INSTANCE;
+ }
+
+ public static interface CreationExtras.Key<T> {
+ }
+
+ @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
+ ctor public InitializerViewModelFactoryBuilder();
+ method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ method public androidx.lifecycle.ViewModelProvider.Factory build();
+ }
+
+ public final class InitializerViewModelFactoryKt {
+ method public static inline <reified VM extends androidx.lifecycle.ViewModel> void initializer(androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+ method public static inline androidx.lifecycle.ViewModelProvider.Factory viewModelFactory(kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit> builder);
+ }
+
+ public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
+ ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
+ method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+ method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T? t);
+ }
+
+ @kotlin.DslMarker public @interface ViewModelFactoryDsl {
+ }
+
+ public final class ViewModelInitializer<T extends androidx.lifecycle.ViewModel> {
+ ctor public ViewModelInitializer(Class<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
+ }
+
+}
+
diff --git a/media/media/api/current.txt b/media/media/api/current.txt
index 88dc4db3..2509d03 100644
--- a/media/media/api/current.txt
+++ b/media/media/api/current.txt
@@ -690,6 +690,7 @@
method public static android.support.v4.media.session.MediaSessionCompat.Token! getMediaSession(android.app.Notification!);
method public androidx.media.app.NotificationCompat.MediaStyle! setCancelButtonIntent(android.app.PendingIntent!);
method public androidx.media.app.NotificationCompat.MediaStyle! setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token!);
+ method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public androidx.media.app.NotificationCompat.MediaStyle setRemotePlaybackInfo(CharSequence, @DrawableRes int, android.app.PendingIntent?);
method public androidx.media.app.NotificationCompat.MediaStyle! setShowActionsInCompactView(int...);
method public androidx.media.app.NotificationCompat.MediaStyle! setShowCancelButton(boolean);
}
diff --git a/media/media/api/restricted_current.txt b/media/media/api/restricted_current.txt
index a40b59b..4f0864c 100644
--- a/media/media/api/restricted_current.txt
+++ b/media/media/api/restricted_current.txt
@@ -728,6 +728,7 @@
method public static android.support.v4.media.session.MediaSessionCompat.Token! getMediaSession(android.app.Notification!);
method public androidx.media.app.NotificationCompat.MediaStyle! setCancelButtonIntent(android.app.PendingIntent!);
method public androidx.media.app.NotificationCompat.MediaStyle! setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token!);
+ method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public androidx.media.app.NotificationCompat.MediaStyle setRemotePlaybackInfo(CharSequence, @DrawableRes int, android.app.PendingIntent?);
method public androidx.media.app.NotificationCompat.MediaStyle! setShowActionsInCompactView(int...);
method public androidx.media.app.NotificationCompat.MediaStyle! setShowCancelButton(boolean);
}
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 783d543..1523950 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -25,6 +25,7 @@
api("androidx.core:core:1.6.0")
implementation("androidx.annotation:annotation:1.2.0")
implementation("androidx.collection:collection:1.1.0")
+ implementation("androidx.core:core:1.9.0")
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
diff --git a/media/media/lint-baseline.xml b/media/media/lint-baseline.xml
index 7760b0e..83d9e78 100644
--- a/media/media/lint-baseline.xml
+++ b/media/media/lint-baseline.xml
@@ -1,5 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.1.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta01)" variant="all" version="8.1.0-beta01">
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
<issue
id="RequireUnstableAidlAnnotation"
diff --git a/media/media/src/main/java/androidx/media/app/NotificationCompat.java b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
index e5c36ad..be90897 100644
--- a/media/media/src/main/java/androidx/media/app/NotificationCompat.java
+++ b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
@@ -16,9 +16,12 @@
package androidx.media.app;
+import static android.Manifest.permission.MEDIA_CONTENT_CONTROL;
+
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.core.app.NotificationCompat.COLOR_DEFAULT;
+import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.media.session.MediaSession;
@@ -31,10 +34,16 @@
import android.widget.RemoteViews;
import androidx.annotation.DoNotInline;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.annotation.RequiresPermission;
import androidx.annotation.RestrictTo;
import androidx.core.app.BundleCompat;
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
+import androidx.core.os.BuildCompat;
import androidx.media.R;
/**
@@ -133,6 +142,10 @@
MediaSessionCompat.Token mToken;
boolean mShowCancelButton;
PendingIntent mCancelButtonIntent;
+ CharSequence mDeviceName;
+ int mDeviceIcon;
+ PendingIntent mDeviceIntent;
+ boolean mShowRemotePlaybackInfo = false;
public MediaStyle() {
}
@@ -162,6 +175,36 @@
}
/**
+ * For media notifications associated with playback on a remote device, provide device
+ * information that will replace the default values for the output switcher chip on the
+ * media control, as well as an intent to use when the output switcher chip is tapped,
+ * on devices where this is supported.
+ * <p>
+ * This method is intended for system applications to provide information and/or
+ * functionality that would otherwise be unavailable to the default output switcher because
+ * the media originated on a remote device.
+ * <p>
+ * Also note that this method is a no-op when running on Tiramisu or less.
+ *
+ * @param deviceName The name of the remote device to display.
+ * @param iconResource Icon resource, of size 12, representing the device.
+ * @param chipIntent PendingIntent to send when the output switcher is tapped. May be
+ * {@code null}, in which case the output switcher will be disabled.
+ * This intent should open an Activity or it will be ignored.
+ * @return MediaStyle
+ */
+ @RequiresPermission(MEDIA_CONTENT_CONTROL)
+ @NonNull
+ public MediaStyle setRemotePlaybackInfo(@NonNull CharSequence deviceName,
+ @DrawableRes int iconResource, @Nullable PendingIntent chipIntent) {
+ mDeviceName = deviceName;
+ mDeviceIcon = iconResource;
+ mDeviceIntent = chipIntent;
+ mShowRemotePlaybackInfo = true;
+ return this;
+ }
+
+ /**
* Sets whether a cancel button at the top right should be shown in the notification on
* platforms before Lollipop.
*
@@ -205,10 +248,17 @@
/**
*/
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
@RestrictTo(LIBRARY)
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
- if (Build.VERSION.SDK_INT >= 21) {
+ if (BuildCompat.isAtLeastU()) {
+ Api21Impl.setMediaStyle(builder.getBuilder(),
+ Api21Impl.fillInMediaStyle(Api34Impl.setRemotePlaybackInfo(
+ Api21Impl.createMediaStyle(), mDeviceName, mDeviceIcon,
+ mDeviceIntent, mShowRemotePlaybackInfo),
+ mActionsToShowInCompact, mToken));
+ } else if (Build.VERSION.SDK_INT >= 21) {
Api21Impl.setMediaStyle(builder.getBuilder(),
Api21Impl.fillInMediaStyle(Api21Impl.createMediaStyle(),
mActionsToShowInCompact, mToken));
@@ -370,10 +420,17 @@
/**
*/
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
@RestrictTo(LIBRARY)
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
- if (Build.VERSION.SDK_INT >= 24) {
+ if (BuildCompat.isAtLeastU()) {
+ Api21Impl.setMediaStyle(builder.getBuilder(), Api21Impl.fillInMediaStyle(
+ Api34Impl.setRemotePlaybackInfo(
+ Api24Impl.createDecoratedMediaCustomViewStyle(), mDeviceName,
+ mDeviceIcon, mDeviceIntent, mShowRemotePlaybackInfo),
+ mActionsToShowInCompact, mToken));
+ } else if (Build.VERSION.SDK_INT >= 24) {
Api21Impl.setMediaStyle(builder.getBuilder(),
Api21Impl.fillInMediaStyle(Api24Impl.createDecoratedMediaCustomViewStyle(),
mActionsToShowInCompact, mToken));
@@ -544,4 +601,24 @@
return new Notification.DecoratedMediaCustomViewStyle();
}
}
-}
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+
+ private Api34Impl() {}
+
+ @SuppressLint({"MissingPermission"})
+ @DoNotInline
+ static Notification.MediaStyle setRemotePlaybackInfo(Notification.MediaStyle style,
+ @NonNull CharSequence deviceName, @DrawableRes int iconResource,
+ @Nullable PendingIntent chipIntent, Boolean showRemotePlaybackInfo) {
+ // Suppress @RequiresPermission(MEDIA_CONTENT_CONTROL) because the API is only used
+ // if showRemotePlaybackInfo is set to true. This only happens for callers to
+ // NotificationCompat#setRemotePlaybackInfo.
+ if (showRemotePlaybackInfo) {
+ style.setRemotePlaybackInfo(deviceName, iconResource, chipIntent);
+ }
+ return style;
+ }
+ }
+}
\ No newline at end of file
diff --git a/mediarouter/mediarouter/api/current.txt b/mediarouter/mediarouter/api/current.txt
index 3fb8c40..00e0b0a 100644
--- a/mediarouter/mediarouter/api/current.txt
+++ b/mediarouter/mediarouter/api/current.txt
@@ -170,8 +170,10 @@
method public android.os.Bundle asBundle();
method public boolean canDisconnectAndKeepPlaying();
method public static androidx.mediarouter.media.MediaRouteDescriptor? fromBundle(android.os.Bundle?);
+ method public java.util.Set<java.lang.String!> getAllowedPackages();
method public int getConnectionState();
method public java.util.List<android.content.IntentFilter!> getControlFilters();
+ method public java.util.Set<java.lang.String!> getDeduplicationIds();
method public String? getDescription();
method public int getDeviceType();
method public android.os.Bundle? getExtras();
@@ -189,6 +191,7 @@
method public boolean isDynamicGroupRoute();
method public boolean isEnabled();
method public boolean isValid();
+ method public boolean isVisibilityPublic();
}
public static final class MediaRouteDescriptor.Builder {
@@ -201,6 +204,7 @@
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
method @Deprecated public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnecting(boolean);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnectionState(int);
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeduplicationIds(java.util.Set<java.lang.String!>);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDescription(String?);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeviceType(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setEnabled(boolean);
@@ -213,6 +217,8 @@
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackType(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender?);
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityPublic();
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityRestricted(java.util.Set<java.lang.String!>);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolume(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeMax(int);
@@ -346,7 +352,7 @@
method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback);
method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback, int);
method @MainThread public void addProvider(androidx.mediarouter.media.MediaRouteProvider);
- method @MainThread public void addRemoteControlClient(Object);
+ method @Deprecated @MainThread public void addRemoteControlClient(Object);
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo? getBluetoothRoute();
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getDefaultRoute();
method @MainThread public static androidx.mediarouter.media.MediaRouter getInstance(android.content.Context);
@@ -363,6 +369,7 @@
method @MainThread public void setMediaSession(Object?);
method @MainThread public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat?);
method @MainThread public void setOnPrepareTransferListener(androidx.mediarouter.media.MediaRouter.OnPrepareTransferListener?);
+ method @MainThread public void setRouteListingPreference(androidx.mediarouter.media.RouteListingPreference?);
method @MainThread public void setRouterParams(androidx.mediarouter.media.MediaRouterParams?);
method @MainThread public void unselect(int);
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo updateSelectedRoute(androidx.mediarouter.media.MediaRouteSelector);
@@ -562,5 +569,53 @@
method public void onSessionStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?);
}
+ public final class RouteListingPreference {
+ method public java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!> getItems();
+ method public android.content.ComponentName? getLinkedItemComponentName();
+ method public boolean getUseSystemOrdering();
+ field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+ field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+ }
+
+ public static final class RouteListingPreference.Builder {
+ ctor public RouteListingPreference.Builder();
+ method public androidx.mediarouter.media.RouteListingPreference build();
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setItems(java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!>);
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setLinkedItemComponentName(android.content.ComponentName?);
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
+ }
+
+ public static final class RouteListingPreference.Item {
+ method public CharSequence? getCustomSubtextMessage();
+ method public int getFlags();
+ method public String getRouteId();
+ method public int getSelectionBehavior();
+ method public int getSubText();
+ field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+ field public static final int FLAG_ONGOING_SESSION_MANAGED = 2; // 0x2
+ field public static final int FLAG_SUGGESTED = 4; // 0x4
+ field public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2; // 0x2
+ field public static final int SELECTION_BEHAVIOR_NONE = 0; // 0x0
+ field public static final int SELECTION_BEHAVIOR_TRANSFER = 1; // 0x1
+ field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4; // 0x4
+ field public static final int SUBTEXT_CUSTOM = 10000; // 0x2710
+ field public static final int SUBTEXT_DEVICE_LOW_POWER = 5; // 0x5
+ field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3; // 0x3
+ field public static final int SUBTEXT_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int SUBTEXT_NONE = 0; // 0x0
+ field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2; // 0x2
+ field public static final int SUBTEXT_TRACK_UNSUPPORTED = 7; // 0x7
+ field public static final int SUBTEXT_UNAUTHORIZED = 6; // 0x6
+ }
+
+ public static final class RouteListingPreference.Item.Builder {
+ ctor public RouteListingPreference.Item.Builder(String);
+ method public androidx.mediarouter.media.RouteListingPreference.Item build();
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setCustomSubtextMessage(CharSequence?);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setFlags(int);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSelectionBehavior(int);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSubText(int);
+ }
+
}
diff --git a/mediarouter/mediarouter/api/restricted_current.txt b/mediarouter/mediarouter/api/restricted_current.txt
index 3fb8c40..00e0b0a 100644
--- a/mediarouter/mediarouter/api/restricted_current.txt
+++ b/mediarouter/mediarouter/api/restricted_current.txt
@@ -170,8 +170,10 @@
method public android.os.Bundle asBundle();
method public boolean canDisconnectAndKeepPlaying();
method public static androidx.mediarouter.media.MediaRouteDescriptor? fromBundle(android.os.Bundle?);
+ method public java.util.Set<java.lang.String!> getAllowedPackages();
method public int getConnectionState();
method public java.util.List<android.content.IntentFilter!> getControlFilters();
+ method public java.util.Set<java.lang.String!> getDeduplicationIds();
method public String? getDescription();
method public int getDeviceType();
method public android.os.Bundle? getExtras();
@@ -189,6 +191,7 @@
method public boolean isDynamicGroupRoute();
method public boolean isEnabled();
method public boolean isValid();
+ method public boolean isVisibilityPublic();
}
public static final class MediaRouteDescriptor.Builder {
@@ -201,6 +204,7 @@
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setCanDisconnect(boolean);
method @Deprecated public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnecting(boolean);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setConnectionState(int);
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeduplicationIds(java.util.Set<java.lang.String!>);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDescription(String?);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setDeviceType(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setEnabled(boolean);
@@ -213,6 +217,8 @@
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPlaybackType(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setPresentationDisplayId(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setSettingsActivity(android.content.IntentSender?);
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityPublic();
+ method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVisibilityRestricted(java.util.Set<java.lang.String!>);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolume(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeHandling(int);
method public androidx.mediarouter.media.MediaRouteDescriptor.Builder setVolumeMax(int);
@@ -346,7 +352,7 @@
method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback);
method @MainThread public void addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback, int);
method @MainThread public void addProvider(androidx.mediarouter.media.MediaRouteProvider);
- method @MainThread public void addRemoteControlClient(Object);
+ method @Deprecated @MainThread public void addRemoteControlClient(Object);
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo? getBluetoothRoute();
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo getDefaultRoute();
method @MainThread public static androidx.mediarouter.media.MediaRouter getInstance(android.content.Context);
@@ -363,6 +369,7 @@
method @MainThread public void setMediaSession(Object?);
method @MainThread public void setMediaSessionCompat(android.support.v4.media.session.MediaSessionCompat?);
method @MainThread public void setOnPrepareTransferListener(androidx.mediarouter.media.MediaRouter.OnPrepareTransferListener?);
+ method @MainThread public void setRouteListingPreference(androidx.mediarouter.media.RouteListingPreference?);
method @MainThread public void setRouterParams(androidx.mediarouter.media.MediaRouterParams?);
method @MainThread public void unselect(int);
method @MainThread public androidx.mediarouter.media.MediaRouter.RouteInfo updateSelectedRoute(androidx.mediarouter.media.MediaRouteSelector);
@@ -562,5 +569,53 @@
method public void onSessionStatusChanged(android.os.Bundle?, String, androidx.mediarouter.media.MediaSessionStatus?);
}
+ public final class RouteListingPreference {
+ method public java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!> getItems();
+ method public android.content.ComponentName? getLinkedItemComponentName();
+ method public boolean getUseSystemOrdering();
+ field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+ field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+ }
+
+ public static final class RouteListingPreference.Builder {
+ ctor public RouteListingPreference.Builder();
+ method public androidx.mediarouter.media.RouteListingPreference build();
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setItems(java.util.List<androidx.mediarouter.media.RouteListingPreference.Item!>);
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setLinkedItemComponentName(android.content.ComponentName?);
+ method public androidx.mediarouter.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
+ }
+
+ public static final class RouteListingPreference.Item {
+ method public CharSequence? getCustomSubtextMessage();
+ method public int getFlags();
+ method public String getRouteId();
+ method public int getSelectionBehavior();
+ method public int getSubText();
+ field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+ field public static final int FLAG_ONGOING_SESSION_MANAGED = 2; // 0x2
+ field public static final int FLAG_SUGGESTED = 4; // 0x4
+ field public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2; // 0x2
+ field public static final int SELECTION_BEHAVIOR_NONE = 0; // 0x0
+ field public static final int SELECTION_BEHAVIOR_TRANSFER = 1; // 0x1
+ field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4; // 0x4
+ field public static final int SUBTEXT_CUSTOM = 10000; // 0x2710
+ field public static final int SUBTEXT_DEVICE_LOW_POWER = 5; // 0x5
+ field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3; // 0x3
+ field public static final int SUBTEXT_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int SUBTEXT_NONE = 0; // 0x0
+ field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2; // 0x2
+ field public static final int SUBTEXT_TRACK_UNSUPPORTED = 7; // 0x7
+ field public static final int SUBTEXT_UNAUTHORIZED = 6; // 0x6
+ }
+
+ public static final class RouteListingPreference.Item.Builder {
+ ctor public RouteListingPreference.Item.Builder(String);
+ method public androidx.mediarouter.media.RouteListingPreference.Item build();
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setCustomSubtextMessage(CharSequence?);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setFlags(int);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSelectionBehavior(int);
+ method public androidx.mediarouter.media.RouteListingPreference.Item.Builder setSubText(int);
+ }
+
}
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index 46de044..3bf9eec 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -25,11 +25,12 @@
api("androidx.media:media:1.4.1")
api(libs.guavaListenableFuture)
- implementation("androidx.core:core:1.6.0")
+ implementation("androidx.core:core:1.8.0")
implementation("androidx.appcompat:appcompat:1.1.0")
implementation("androidx.palette:palette:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.1.0")
implementation("androidx.appcompat:appcompat-resources:1.2.0")
+ implementation "androidx.annotation:annotation-experimental:1.3.0"
testImplementation(libs.junit)
testImplementation(libs.testCore)
@@ -41,6 +42,7 @@
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.truth)
androidTestImplementation(libs.espressoCore, excludes.espresso)
androidTestImplementation(project(":media:version-compat-tests:lib"))
androidTestImplementation(project(":mediarouter:mediarouter-testing"))
diff --git a/mediarouter/mediarouter/lint-baseline.xml b/mediarouter/mediarouter/lint-baseline.xml
index fd567a4..8a9547e 100644
--- a/mediarouter/mediarouter/lint-baseline.xml
+++ b/mediarouter/mediarouter/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.1.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta01)" variant="all" version="8.1.0-beta01">
<issue
id="NewApi"
@@ -20,6 +20,51 @@
</issue>
<issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" ProviderInfo(MediaRouteProvider provider, boolean treatRouteDescriptorIdsAsUnique) {"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRouter.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" android.media.RouteListingPreference toPlatformRouteListingPreference() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/RouteListingPreference.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" public @interface SelectionBehavior {}"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/RouteListingPreference.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" public @interface Flags {}"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/RouteListingPreference.java"/>
+ </issue>
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1=" public @interface SubText {}"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/RouteListingPreference.java"/>
+ </issue>
+
+ <issue
id="BanThreadSleep"
message="Uses Thread.sleep()"
errorLine1=" Thread.sleep(TIME_OUT_MS);"
@@ -91,4 +136,40 @@
file="src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java"/>
</issue>
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (mMr2Provider != null && BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRouter.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java"/>
+ </issue>
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java"/>
+ </issue>
+
</issues>
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteDescriptorTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteDescriptorTest.java
index 504caff..0d3b08c 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteDescriptorTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteDescriptorTest.java
@@ -17,9 +17,12 @@
package androidx.mediarouter.media;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import android.content.IntentFilter;
+import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -28,7 +31,9 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Test for {@link MediaRouteDescriptor}.
@@ -43,6 +48,7 @@
private static final String FAKE_MEDIA_ROUTE_ID_4 = "fakeMediaRouteId4";
private static final String FAKE_CONTROL_ACTION_1 = "fakeControlAction1";
private static final String FAKE_CONTROL_ACTION_2 = "fakeControlAction2";
+ private static final String FAKE_PACKAGE_NAME = "com.sample.example";
@Test
@SmallTest
@@ -102,4 +108,114 @@
final List<IntentFilter> controlFilters2 = routeDescriptor.getControlFilters();
assertTrue(controlFilters2.isEmpty());
}
+
+ @Test
+ @SmallTest
+ public void testDefaultVisibilityIsPublic() {
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_ID_1, FAKE_MEDIA_ROUTE_NAME)
+ .build();
+
+ assertTrue(routeDescriptor.isVisibilityPublic());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsVisibilityRestricted() {
+ Set<String> allowedPackages = new HashSet<>();
+ allowedPackages.add(FAKE_PACKAGE_NAME);
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_ID_1, FAKE_MEDIA_ROUTE_NAME)
+ .setVisibilityRestricted(allowedPackages)
+ .build();
+
+ assertFalse(routeDescriptor.isVisibilityPublic());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetAllowedPackagesReturnsNewInstance() {
+ Set<String> sampleAllowedPackages = new HashSet<>();
+ sampleAllowedPackages.add(FAKE_PACKAGE_NAME);
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_ID_1, FAKE_MEDIA_ROUTE_NAME)
+ .setVisibilityRestricted(sampleAllowedPackages)
+ .build();
+
+ Set<String> allowedPackages = routeDescriptor.getAllowedPackages();
+
+ assertEquals(sampleAllowedPackages, allowedPackages);
+ assertNotSame(sampleAllowedPackages, allowedPackages);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetControlFiltersReturnsNewInstance() {
+ IntentFilter f1 = new IntentFilter();
+ f1.addCategory("com.example.androidx.media.CATEGORY_SAMPLE_ROUTE");
+ f1.addAction("com.example.androidx.media.action.TAKE_SNAPSHOT");
+
+ IntentFilter f2 = new IntentFilter();
+ f2.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+ f2.addAction(MediaControlIntent.ACTION_PLAY);
+ f2.addDataScheme("http");
+ f2.addDataScheme("https");
+ f2.addDataScheme("rtsp");
+ f2.addDataScheme("file");
+
+ IntentFilter f3 = new IntentFilter();
+ f3.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+ f3.addAction(MediaControlIntent.ACTION_SEEK);
+ f3.addAction(MediaControlIntent.ACTION_GET_STATUS);
+ f3.addAction(MediaControlIntent.ACTION_PAUSE);
+ f3.addAction(MediaControlIntent.ACTION_RESUME);
+ f3.addAction(MediaControlIntent.ACTION_STOP);
+
+ List<IntentFilter> sampleControlFilters = new ArrayList<>();
+ sampleControlFilters.add(f1);
+ sampleControlFilters.add(f2);
+ sampleControlFilters.add(f3);
+
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_ID_1, FAKE_MEDIA_ROUTE_NAME)
+ .addControlFilter(f1)
+ .addControlFilter(f2)
+ .addControlFilter(f3)
+ .build();
+
+ List<IntentFilter> controlFilters = routeDescriptor.getControlFilters();
+
+ assertEquals(sampleControlFilters, controlFilters);
+ assertNotSame(sampleControlFilters, controlFilters);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetGroupMemberIdsReturnsNewInstance() {
+ List<String> sampleGroupMemberIds = new ArrayList<>();
+ sampleGroupMemberIds.add(FAKE_MEDIA_ROUTE_ID_2);
+ sampleGroupMemberIds.add(FAKE_MEDIA_ROUTE_ID_3);
+ sampleGroupMemberIds.add(FAKE_MEDIA_ROUTE_ID_4);
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_ID_1, FAKE_MEDIA_ROUTE_NAME)
+ .addGroupMemberId(FAKE_MEDIA_ROUTE_ID_2)
+ .addGroupMemberId(FAKE_MEDIA_ROUTE_ID_3)
+ .addGroupMemberId(FAKE_MEDIA_ROUTE_ID_4)
+ .build();
+
+ List<String> groupMemberIds = routeDescriptor.getGroupMemberIds();
+
+ assertEquals(sampleGroupMemberIds, groupMemberIds);
+ assertNotSame(sampleGroupMemberIds, groupMemberIds);
+ }
+
+ @Test
+ @SmallTest
+ public void testConstructorUsingBundleReturnsEmptyCollections() {
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor(new Bundle());
+
+ assertTrue(routeDescriptor.getAllowedPackages().isEmpty());
+ assertTrue(routeDescriptor.getControlFilters().isEmpty());
+ assertTrue(routeDescriptor.getGroupMemberIds().isEmpty());
+ }
}
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2UtilsTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2UtilsTest.java
index ae769ed..776a596 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2UtilsTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2UtilsTest.java
@@ -16,11 +16,17 @@
package androidx.mediarouter.media;
+import static androidx.mediarouter.media.MediaRouter2Utils.KEY_CONTROL_FILTERS;
+import static androidx.mediarouter.media.MediaRouter2Utils.KEY_DEVICE_TYPE;
+import static androidx.mediarouter.media.MediaRouter2Utils.KEY_EXTRAS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.media.MediaRoute2Info;
import android.os.Build;
+import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
@@ -29,6 +35,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.HashSet;
+
/** Test for {@link MediaRouter2Utils}. */
@SmallTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
@@ -37,6 +46,19 @@
private static final String FAKE_MEDIA_ROUTE_DESCRIPTOR_ID = "fake_id";
private static final String FAKE_MEDIA_ROUTE_DESCRIPTOR_NAME = "fake_name";
+ /**
+ * Placeholder bundle for {@link MediaRouter2Utils#toMediaRouteDescriptor} to accept passed
+ * {@link MediaRoute2Info} instances as valid.
+ */
+ private static final Bundle PLACEHOLDER_EXTRAS_BUNDLE;
+
+ static {
+ PLACEHOLDER_EXTRAS_BUNDLE = new Bundle();
+ PLACEHOLDER_EXTRAS_BUNDLE.putBundle(KEY_EXTRAS, new Bundle());
+ PLACEHOLDER_EXTRAS_BUNDLE.putInt(
+ KEY_DEVICE_TYPE, MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN);
+ PLACEHOLDER_EXTRAS_BUNDLE.putParcelableArrayList(KEY_CONTROL_FILTERS, new ArrayList<>());
+ }
@Test
public void toFwkMediaRoute2Info_withValidDescriptor_returnsInstance() {
@@ -60,4 +82,70 @@
.build();
assertNull(MediaRouter2Utils.toFwkMediaRoute2Info(descriptorWithEmptyName));
}
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void toFwkMediaRoute2Info_withDeduplicationIds() {
+ HashSet<String> dedupIds = new HashSet<>();
+ dedupIds.add("dedup_id1");
+ dedupIds.add("dedup_id2");
+ MediaRouteDescriptor descriptor =
+ new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_DESCRIPTOR_ID, FAKE_MEDIA_ROUTE_DESCRIPTOR_NAME)
+ .setDeduplicationIds(dedupIds)
+ .build();
+ assertTrue(
+ MediaRouter2Utils.toFwkMediaRoute2Info(descriptor)
+ .getDeduplicationIds()
+ .equals(dedupIds));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void toMediaRouteDescriptor_withDeduplicationIds() {
+ HashSet<String> dedupIds = new HashSet<>();
+ dedupIds.add("dedup_id1");
+ dedupIds.add("dedup_id2");
+
+ MediaRoute2Info routeInfo =
+ new MediaRoute2Info.Builder(
+ FAKE_MEDIA_ROUTE_DESCRIPTOR_ID, FAKE_MEDIA_ROUTE_DESCRIPTOR_NAME)
+ .addFeature(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
+ .setDeduplicationIds(dedupIds)
+ .setExtras(PLACEHOLDER_EXTRAS_BUNDLE)
+ .build();
+ assertTrue(
+ MediaRouter2Utils.toMediaRouteDescriptor(routeInfo)
+ .getDeduplicationIds()
+ .equals(dedupIds));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void toMediaRouteDescriptor_withDeviceType_setsCorrectType() {
+ MediaRoute2Info routeInfo =
+ new MediaRoute2Info.Builder(
+ FAKE_MEDIA_ROUTE_DESCRIPTOR_ID, FAKE_MEDIA_ROUTE_DESCRIPTOR_NAME)
+ .addFeature(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
+ .setType(MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER)
+ .setExtras(PLACEHOLDER_EXTRAS_BUNDLE)
+ .build();
+
+ assertEquals(
+ MediaRouter.RouteInfo.DEVICE_TYPE_AUDIO_VIDEO_RECEIVER,
+ MediaRouter2Utils.toMediaRouteDescriptor(routeInfo).getDeviceType());
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void toFwkMediaRoute2Info_withType_setsCorrectDeviceType() {
+ MediaRouteDescriptor descriptor =
+ new MediaRouteDescriptor.Builder(
+ FAKE_MEDIA_ROUTE_DESCRIPTOR_ID, FAKE_MEDIA_ROUTE_DESCRIPTOR_NAME)
+ .setDeviceType(MediaRouter.RouteInfo.DEVICE_TYPE_TV)
+ .build();
+ assertEquals(
+ MediaRoute2Info.TYPE_REMOTE_TV,
+ MediaRouter2Utils.toFwkMediaRoute2Info(descriptor).getType());
+ }
}
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/RouteListingPreferenceTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/RouteListingPreferenceTest.java
new file mode 100644
index 0000000..e99c4e9
--- /dev/null
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/RouteListingPreferenceTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.mediarouter.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class RouteListingPreferenceTest {
+
+ private static final String FAKE_ROUTE_ID = "fake_id";
+ private static final String FAKE_CUSTOM_SUBTEXT = "a custom subtext";
+ private static final ComponentName FAKE_COMPONENT_NAME =
+ new ComponentName(
+ ApplicationProvider.getApplicationContext(), RouteListingPreferenceTest.class);
+
+ private Context mContext;
+ private MediaRouter mMediaRouterUnderTest;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(() -> mMediaRouterUnderTest = MediaRouter.getInstance(mContext));
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(
+ () ->
+ mMediaRouterUnderTest.setRouteListingPreference(
+ /* routeListingPreference= */ null));
+ }
+
+ @SmallTest
+ @Test
+ public void setRouteListingPreference_onAnyApiLevel_doesNotCrash() {
+ // AndroidX infra runs tests on all API levels with significant usage, hence this test
+ // checks this call does not crash regardless of whether route listing preference symbols
+ // are defined on the current platform level.
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(
+ () ->
+ mMediaRouterUnderTest.setRouteListingPreference(
+ new RouteListingPreference.Builder().build()));
+ }
+
+ @SmallTest
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ public void routeListingPreference_yieldsExpectedPlatformEquivalent() {
+ RouteListingPreference.Item fakeRlpItem =
+ new RouteListingPreference.Item.Builder(FAKE_ROUTE_ID)
+ .setFlags(RouteListingPreference.Item.FLAG_SUGGESTED)
+ .setSubText(RouteListingPreference.Item.SUBTEXT_CUSTOM)
+ .setCustomSubtextMessage(FAKE_CUSTOM_SUBTEXT)
+ .setSelectionBehavior(
+ RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP)
+ .build();
+ RouteListingPreference fakeRouteListingPreference =
+ new RouteListingPreference.Builder()
+ .setItems(Collections.singletonList(fakeRlpItem))
+ .setLinkedItemComponentName(FAKE_COMPONENT_NAME)
+ .setUseSystemOrdering(false)
+ .build();
+ android.media.RouteListingPreference platformRlp =
+ fakeRouteListingPreference.toPlatformRouteListingPreference();
+
+ assertThat(platformRlp.getUseSystemOrdering()).isFalse();
+ assertThat(platformRlp.getLinkedItemComponentName()).isEqualTo(FAKE_COMPONENT_NAME);
+
+ List<android.media.RouteListingPreference.Item> platformRlpItems = platformRlp.getItems();
+ assertThat(platformRlpItems).hasSize(1);
+ android.media.RouteListingPreference.Item platformRlpItem = platformRlpItems.get(0);
+ assertThat(platformRlpItem.getRouteId()).isEqualTo(FAKE_ROUTE_ID);
+ assertThat(platformRlpItem.getFlags())
+ .isEqualTo(RouteListingPreference.Item.FLAG_SUGGESTED);
+ assertThat(platformRlpItem.getSelectionBehavior())
+ .isEqualTo(RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP);
+ assertThat(platformRlpItem.getSubText())
+ .isEqualTo(android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM);
+ assertThat(platformRlpItem.getCustomSubtextMessage()).isEqualTo(FAKE_CUSTOM_SUBTEXT);
+ }
+}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
index 9782159..3e74545 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
@@ -22,10 +22,13 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.media.MediaRouter2;
import android.os.Build;
import android.provider.Settings;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import java.util.List;
@@ -77,8 +80,10 @@
public static boolean showDialog(@NonNull Context context) {
boolean result = false;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- result = showDialogForAndroidSAndAbove(context)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ result = showDialogForAndroidUAndAbove(context);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ result = showDialogForAndroidSAndT(context)
// The intent action and related string constants are changed in S,
// however they are not public API yet. Try opening the output switcher with the
// old constants for devices that have prior version of the constants.
@@ -98,7 +103,18 @@
return false;
}
- private static boolean showDialogForAndroidSAndAbove(@NonNull Context context) {
+ private static boolean showDialogForAndroidUAndAbove(@NonNull Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ MediaRouter2 mediaRouter2 = Api30Impl.getInstance(context);
+ if (Build.VERSION.SDK_INT >= 34) {
+ return Api34Impl.showSystemOutputSwitcher(mediaRouter2);
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean showDialogForAndroidSAndT(@NonNull Context context) {
Intent intent = new Intent()
.setAction(OUTPUT_SWITCHER_INTENT_ACTION_ANDROID_S)
.setPackage(PACKAGE_NAME_SYSTEM_UI)
@@ -182,4 +198,28 @@
PackageManager packageManager = context.getPackageManager();
return packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
}
+
+ @RequiresApi(30)
+ static class Api30Impl {
+ private Api30Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static MediaRouter2 getInstance(Context context) {
+ return MediaRouter2.getInstance(context);
+ }
+ }
+
+ @RequiresApi(34)
+ static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static boolean showSystemOutputSwitcher(MediaRouter2 mediaRouter2) {
+ return mediaRouter2.showSystemOutputSwitcher();
+ }
+ }
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
index e77626e..f41e069 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
@@ -44,9 +44,12 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.core.os.BuildCompat;
import androidx.mediarouter.R;
import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor;
import androidx.mediarouter.media.MediaRouter.ControlRequestCallback;
@@ -72,7 +75,7 @@
final Callback mCallback;
final Map<MediaRouter2.RoutingController, GroupRouteController> mControllerMap =
new ArrayMap<>();
- private final MediaRouter2.RouteCallback mRouteCallback = new RouteCallback();
+ private final MediaRouter2.RouteCallback mRouteCallback;
private final MediaRouter2.TransferCallback mTransferCallback = new TransferCallback();
private final MediaRouter2.ControllerCallback mControllerCallback = new ControllerCallback();
private final Handler mHandler;
@@ -81,6 +84,8 @@
private List<MediaRoute2Info> mRoutes = new ArrayList<>();
private Map<String, String> mRouteIdToOriginalRouteIdMap = new ArrayMap<>();
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @SuppressWarnings({"SyntheticAccessor"})
MediaRoute2Provider(@NonNull Context context, @NonNull Callback callback) {
super(context);
mMediaRouter2 = MediaRouter2.getInstance(context);
@@ -88,6 +93,12 @@
mHandler = new Handler(Looper.getMainLooper());
mHandlerExecutor = mHandler::post;
+
+ if (BuildCompat.isAtLeastU()) {
+ mRouteCallback = new RouteCallbackUpsideDownCake();
+ } else {
+ mRouteCallback = new RouteCallback();
+ }
}
@Override
@@ -353,6 +364,16 @@
return new MediaRouteDiscoveryRequest(selector, request.isActiveScan());
}
+ @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ /* package */ void setRouteListingPreference(
+ @Nullable RouteListingPreference routeListingPreference) {
+ Api34Impl.setPlatformRouteListingPreference(
+ mMediaRouter2,
+ routeListingPreference != null
+ ? routeListingPreference.toPlatformRouteListingPreference()
+ : null);
+ }
+
abstract static class Callback {
public abstract void onSelectRoute(@NonNull String routeDescriptorId,
@MediaRouter.UnselectReason int reason);
@@ -380,6 +401,14 @@
}
}
+ private class RouteCallbackUpsideDownCake extends MediaRouter2.RouteCallback {
+
+ @Override
+ public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {
+ refreshRoutes();
+ }
+ }
+
private class TransferCallback extends MediaRouter2.TransferCallback {
TransferCallback() {}
@@ -695,4 +724,18 @@
}
}
}
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setPlatformRouteListingPreference(
+ @NonNull MediaRouter2 mediaRouter2,
+ @Nullable android.media.RouteListingPreference routeListingPreference) {
+ mediaRouter2.setRouteListingPreference(routeListingPreference);
+ }
+ }
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteDescriptor.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteDescriptor.java
index a202f44..e053d4f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteDescriptor.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteDescriptor.java
@@ -17,8 +17,10 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import android.annotation.SuppressLint;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.media.RouteDiscoveryPreference;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
@@ -31,7 +33,9 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Describes the properties of a route.
@@ -65,10 +69,11 @@
static final String KEY_SETTINGS_INTENT = "settingsIntent";
static final String KEY_MIN_CLIENT_VERSION = "minClientVersion";
static final String KEY_MAX_CLIENT_VERSION = "maxClientVersion";
+ static final String KEY_DEDUPLICATION_IDS = "deduplicationIds";
+ static final String KEY_IS_VISIBILITY_PUBLIC = "isVisibilityPublic";
+ static final String KEY_ALLOWED_PACKAGES = "allowedPackages";
final Bundle mBundle;
- List<String> mGroupMemberIds;
- List<IntentFilter> mControlFilters;
MediaRouteDescriptor(Bundle bundle) {
mBundle = bundle;
@@ -97,17 +102,10 @@
@RestrictTo(LIBRARY)
@NonNull
public List<String> getGroupMemberIds() {
- ensureGroupMemberIds();
- return mGroupMemberIds;
- }
-
- void ensureGroupMemberIds() {
- if (mGroupMemberIds == null) {
- mGroupMemberIds = mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS);
- if (mGroupMemberIds == null) {
- mGroupMemberIds = Collections.emptyList();
- }
+ if (!mBundle.containsKey(KEY_GROUP_MEMBER_IDS)) {
+ return new ArrayList<>();
}
+ return new ArrayList<>(mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS));
}
/**
@@ -229,17 +227,10 @@
*/
@NonNull
public List<IntentFilter> getControlFilters() {
- ensureControlFilters();
- return mControlFilters;
- }
-
- void ensureControlFilters() {
- if (mControlFilters == null) {
- mControlFilters = mBundle.getParcelableArrayList(KEY_CONTROL_FILTERS);
- if (mControlFilters == null) {
- mControlFilters = Collections.emptyList();
- }
+ if (!mBundle.containsKey(KEY_CONTROL_FILTERS)) {
+ return new ArrayList<>();
}
+ return new ArrayList<>(mBundle.getParcelableArrayList(KEY_CONTROL_FILTERS));
}
/**
@@ -297,6 +288,20 @@
}
/**
+ * Gets the route's deduplication ids.
+ *
+ * <p>Two routes are considered to come from the same receiver device if any of their respective
+ * deduplication ids match.
+ */
+ @NonNull
+ public Set<String> getDeduplicationIds() {
+ ArrayList<String> deduplicationIds = mBundle.getStringArrayList(KEY_DEDUPLICATION_IDS);
+ return deduplicationIds != null
+ ? Collections.unmodifiableSet(new HashSet<>(deduplicationIds))
+ : Collections.emptySet();
+ }
+
+ /**
* Gets the route's presentation display id, or -1 if none.
*/
public int getPresentationDisplayId() {
@@ -332,13 +337,32 @@
}
/**
+ * Gets whether the route visibility is public or not.
+ */
+ public boolean isVisibilityPublic() {
+ return mBundle.getBoolean(KEY_IS_VISIBILITY_PUBLIC, /* defaultValue= */ true);
+ }
+
+ /**
+ * Gets the set of allowed packages which are able to see the route or an empty set if only
+ * the route provider's package is allowed to see this route. This applies only when
+ * {@link #isVisibilityPublic} returns {@code false}.
+ */
+ @NonNull
+ public Set<String> getAllowedPackages() {
+ if (!mBundle.containsKey(KEY_ALLOWED_PACKAGES)) {
+ return new HashSet<>();
+ }
+ return new HashSet<>(mBundle.getStringArrayList(KEY_ALLOWED_PACKAGES));
+ }
+
+ /**
* Returns true if the route descriptor has all of the required fields.
*/
public boolean isValid() {
- ensureControlFilters();
if (TextUtils.isEmpty(getId())
|| TextUtils.isEmpty(getName())
- || mControlFilters.contains(null)) {
+ || getControlFilters().contains(null)) {
return false;
}
return true;
@@ -367,6 +391,8 @@
+ ", isValid=" + isValid()
+ ", minClientVersion=" + getMinClientVersion()
+ ", maxClientVersion=" + getMaxClientVersion()
+ + ", isVisibilityPublic=" + isVisibilityPublic()
+ + ", allowedPackages=" + Arrays.toString(getAllowedPackages().toArray())
+ " }";
}
@@ -396,8 +422,10 @@
*/
public static final class Builder {
private final Bundle mBundle;
- private ArrayList<String> mGroupMemberIds;
- private ArrayList<IntentFilter> mControlFilters;
+
+ private List<String> mGroupMemberIds = new ArrayList<>();
+ private List<IntentFilter> mControlFilters = new ArrayList<>();
+ private Set<String> mAllowedPackages = new HashSet<>();
/**
* Creates a media route descriptor builder.
@@ -422,13 +450,9 @@
mBundle = new Bundle(descriptor.mBundle);
- if (!descriptor.getGroupMemberIds().isEmpty()) {
- mGroupMemberIds = new ArrayList<String>(descriptor.getGroupMemberIds());
- }
-
- if (!descriptor.getControlFilters().isEmpty()) {
- mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters);
- }
+ mGroupMemberIds = descriptor.getGroupMemberIds();
+ mControlFilters = descriptor.getControlFilters();
+ mAllowedPackages = descriptor.getAllowedPackages();
}
/**
@@ -454,9 +478,7 @@
@RestrictTo(LIBRARY)
@NonNull
public Builder clearGroupMemberIds() {
- if (mGroupMemberIds != null) {
- mGroupMemberIds.clear();
- }
+ mGroupMemberIds.clear();
return this;
}
@@ -474,9 +496,6 @@
throw new IllegalArgumentException("groupMemberId must not be empty");
}
- if (mGroupMemberIds == null) {
- mGroupMemberIds = new ArrayList<>();
- }
if (!mGroupMemberIds.contains(groupMemberId)) {
mGroupMemberIds.add(groupMemberId);
}
@@ -518,10 +537,7 @@
if (TextUtils.isEmpty(memberRouteId)) {
throw new IllegalArgumentException("memberRouteId must not be empty");
}
-
- if (mGroupMemberIds != null) {
- mGroupMemberIds.remove(memberRouteId);
- }
+ mGroupMemberIds.remove(memberRouteId);
return this;
}
@@ -599,6 +615,7 @@
mBundle.putBoolean(IS_DYNAMIC_GROUP_ROUTE, isDynamicGroupRoute);
return this;
}
+
/**
* Sets whether the route is in the process of connecting and is not yet
* ready for use.
@@ -649,9 +666,7 @@
*/
@NonNull
public Builder clearControlFilters() {
- if (mControlFilters != null) {
- mControlFilters.clear();
- }
+ mControlFilters.clear();
return this;
}
@@ -664,9 +679,6 @@
throw new IllegalArgumentException("filter must not be null");
}
- if (mControlFilters == null) {
- mControlFilters = new ArrayList<IntentFilter>();
- }
if (!mControlFilters.contains(filter)) {
mControlFilters.add(filter);
}
@@ -757,6 +769,21 @@
}
/**
+ * Sets the route's deduplication ids.
+ *
+ * <p>Two routes are considered to come from the same receiver device if any of their
+ * respective deduplication ids match.
+ *
+ * @param deduplicationIds A set of strings that uniquely identify the receiver device that
+ * backs this route.
+ */
+ @NonNull
+ public Builder setDeduplicationIds(@NonNull Set<String> deduplicationIds) {
+ mBundle.putStringArrayList(KEY_DEDUPLICATION_IDS, new ArrayList<>(deduplicationIds));
+ return this;
+ }
+
+ /**
* Sets the route's presentation display id, or -1 if none.
*/
@NonNull
@@ -803,16 +830,54 @@
}
/**
+ * Sets the visibility of this route to public.
+ *
+ * <p>By default, unless you call {@link #setVisibilityRestricted}, the new route will be
+ * public.
+ *
+ * <p>Public routes are visible to any application with a matching {@link
+ * RouteDiscoveryPreference#getPreferredFeatures feature}.
+ *
+ * <p>Calls to this method override previous calls to {@link #setVisibilityPublic} and
+ * {@link #setVisibilityRestricted}.
+ */
+ @NonNull
+ @SuppressLint({"MissingGetterMatchingBuilder"})
+ public Builder setVisibilityPublic() {
+ mBundle.putBoolean(KEY_IS_VISIBILITY_PUBLIC, true);
+ mAllowedPackages.clear();
+ return this;
+ }
+
+ /**
+ * Sets the visibility of this route to restricted.
+ *
+ * <p>Routes with restricted visibility are only visible to its publisher application and
+ * applications whose package name is included in the provided {@code allowedPackages} set
+ * with a matching {@link RouteDiscoveryPreference#getPreferredFeatures feature}.
+ *
+ * <p>Calls to this method override previous calls to {@link #setVisibilityPublic} and
+ * {@link #setVisibilityRestricted}.
+ *
+ * @see #setVisibilityPublic
+ * @param allowedPackages set of package names which are allowed to see this route.
+ */
+ @NonNull
+ @SuppressLint({"MissingGetterMatchingBuilder"})
+ public Builder setVisibilityRestricted(@NonNull Set<String> allowedPackages) {
+ mBundle.putBoolean(KEY_IS_VISIBILITY_PUBLIC, false);
+ mAllowedPackages = new HashSet<>(allowedPackages);
+ return this;
+ }
+
+ /**
* Builds the {@link MediaRouteDescriptor media route descriptor}.
*/
@NonNull
public MediaRouteDescriptor build() {
- if (mControlFilters != null) {
- mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters);
- }
- if (mGroupMemberIds != null) {
- mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds);
- }
+ mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, new ArrayList<>(mControlFilters));
+ mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, new ArrayList<>(mGroupMemberIds));
+ mBundle.putStringArrayList(KEY_ALLOWED_PACKAGES, new ArrayList<>(mAllowedPackages));
return new MediaRouteDescriptor(mBundle);
}
}
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 dd04930..1e96d25 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -45,12 +45,14 @@
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
import androidx.core.app.ActivityManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.core.hardware.display.DisplayManagerCompat;
+import androidx.core.os.BuildCompat;
import androidx.core.util.ObjectsCompat;
import androidx.core.util.Pair;
import androidx.media.VolumeProviderCompat;
@@ -899,19 +901,19 @@
}
/**
- * Adds a remote control client to enable remote control of the volume
- * of the selected route.
- * <p>
- * The remote control client must have previously been registered with
- * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
+ * Adds a remote control client to enable remote control of the volume of the selected route.
+ *
+ * <p>The remote control client must have previously been registered with the audio manager
+ * using the {@link android.media.AudioManager#registerRemoteControlClient
* AudioManager.registerRemoteControlClient} method.
- * </p>
*
* <p>Must be called on the main thread.
*
* @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
+ * @deprecated Use {@link #setMediaSessionCompat} instead.
*/
@MainThread
+ @Deprecated
public void addRemoteControlClient(@NonNull Object remoteControlClient) {
if (remoteControlClient == null) {
throw new IllegalArgumentException("remoteControlClient must not be null");
@@ -946,14 +948,8 @@
}
/**
- * Sets the media session to enable remote control of the volume of the
- * selected route. This should be used instead of
- * {@link #addRemoteControlClient} when using media sessions. Set the
- * session to null to clear it.
- *
- * <p>Must be called on the main thread.
- *
- * @param mediaSession The {@link android.media.session.MediaSession} to use.
+ * Equivalent to {@link #setMediaSessionCompat}, except it takes an {@link
+ * android.media.session.MediaSession}.
*/
@MainThread
public void setMediaSession(@Nullable Object mediaSession) {
@@ -965,14 +961,16 @@
}
/**
- * Sets a compat media session to enable remote control of the volume of the
- * selected route. This should be used instead of
- * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}.
- * Set the session to null to clear it.
+ * Associates the provided {@link MediaSessionCompat} to this router.
+ *
+ * <p>Maintains the internal state of the provided session to signal it's linked to the
+ * currently selected route at any given time. This guarantees that the system UI shows the
+ * correct route name when applicable.
*
* <p>Must be called on the main thread.
*
- * @param mediaSession The {@link MediaSessionCompat} to use.
+ * @param mediaSession The {@link MediaSessionCompat} to associate to this media router, or null
+ * to clear the existing association.
*/
@MainThread
public void setMediaSessionCompat(@Nullable MediaSessionCompat mediaSession) {
@@ -1018,6 +1016,41 @@
}
/**
+ * Sets the {@link RouteListingPreference} of the app associated to this media router.
+ *
+ * <p>This method does nothing on devices running API 33 or older.
+ *
+ * <p>Use this method to inform the system UI of the routes that you would like to list for
+ * media routing, via the Output Switcher.
+ *
+ * <p>You should call this method immediately after creating an instance and immediately after
+ * receiving any {@link Callback route list changes} in order to keep the system UI in a
+ * consistent state. You can also call this method at any other point to update the listing
+ * preference dynamically (which reflect in the system's Output Switcher).
+ *
+ * <p>Notes:
+ *
+ * <ul>
+ * <li>You should not include the ids of two or more routes with a match in their {@link
+ * MediaRouteDescriptor#getDeduplicationIds() deduplication ids}. If you do, the system
+ * will deduplicate them using its own criteria.
+ * <li>You can use this method to rank routes in the output switcher, placing the more
+ * important routes first. The system might override the proposed ranking.
+ * <li>You can use this method to change how routes are listed using dynamic criteria. For
+ * example, you can disable routing while an {@link
+ * RouteListingPreference.Item#SUBTEXT_AD_ROUTING_DISALLOWED ad is playing}).
+ * </ul>
+ *
+ * @param routeListingPreference The {@link RouteListingPreference} for the system to use for
+ * route listing. When null, the system uses its default listing criteria.
+ */
+ @MainThread
+ public void setRouteListingPreference(@Nullable RouteListingPreference routeListingPreference) {
+ checkCallingThread();
+ getGlobalRouter().setRouteListingPreference(routeListingPreference);
+ }
+
+ /**
* Throws an {@link IllegalStateException} if the calling thread is not the main thread.
*/
static void checkCallingThread() {
@@ -2194,15 +2227,23 @@
* </p>
*/
public static final class ProviderInfo {
+ // Package private fields to avoid use of a synthetic accessor.
final MediaRouteProvider mProviderInstance;
final List<RouteInfo> mRoutes = new ArrayList<>();
+ final boolean mTreatRouteDescriptorIdsAsUnique;
private final ProviderMetadata mMetadata;
private MediaRouteProviderDescriptor mDescriptor;
ProviderInfo(MediaRouteProvider provider) {
+ this(provider, /* treatRouteDescriptorIdsAsUnique= */ false);
+ }
+
+ /** @hide */
+ ProviderInfo(MediaRouteProvider provider, boolean treatRouteDescriptorIdsAsUnique) {
mProviderInstance = provider;
mMetadata = provider.getMetadata();
+ mTreatRouteDescriptorIdsAsUnique = treatRouteDescriptorIdsAsUnique;
}
/**
@@ -2675,9 +2716,9 @@
updateDiscoveryRequest();
}
});
- addProvider(mSystemProvider);
+ addProvider(mSystemProvider, /* treatRouteDescriptorIdsAsUnique= */ true);
if (mMr2Provider != null) {
- addProvider(mMr2Provider);
+ addProvider(mMr2Provider, /* treatRouteDescriptorIdsAsUnique= */ true);
}
// Start watching for routes published by registered media route
@@ -2694,6 +2735,8 @@
mRegisteredProviderWatcher.stop();
mActiveScanThrottlingHelper.reset();
+ setRouteListingPreference(null);
+
setMediaSessionCompat(null);
for (RemoteControlClientRecord record : mRemoteControlClients) {
record.disconnect();
@@ -2812,7 +2855,7 @@
if (mMr2Provider == null) {
mMr2Provider = new MediaRoute2Provider(
mApplicationContext, new Mr2ProviderCallback());
- addProvider(mMr2Provider);
+ addProvider(mMr2Provider, /* treatRouteDescriptorIdsAsUnique= */ true);
// Make sure mDiscoveryRequestForMr2Provider is updated
updateDiscoveryRequest();
mRegisteredProviderWatcher.rescan();
@@ -2838,6 +2881,14 @@
mCallbackHandler.post(CallbackHandler.MSG_ROUTER_PARAMS_CHANGED, params);
}
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public void setRouteListingPreference(
+ @Nullable RouteListingPreference routeListingPreference) {
+ if (mMr2Provider != null && BuildCompat.isAtLeastU()) {
+ mMr2Provider.setRouteListingPreference(routeListingPreference);
+ }
+ }
+
@Nullable
List<ProviderInfo> getProviders() {
return mProviders;
@@ -3120,12 +3171,18 @@
MediaRouterParams.ENABLE_GROUP_VOLUME_UX, true);
}
-
@Override
public void addProvider(@NonNull MediaRouteProvider providerInstance) {
+ addProvider(providerInstance, /* treatRouteDescriptorIdsAsUnique= */ false);
+ }
+
+ private void addProvider(
+ @NonNull MediaRouteProvider providerInstance,
+ boolean treatRouteDescriptorIdsAsUnique) {
if (findProviderInfo(providerInstance) == null) {
// 1. Add the provider to the list.
- ProviderInfo provider = new ProviderInfo(providerInstance);
+ ProviderInfo provider =
+ new ProviderInfo(providerInstance, treatRouteDescriptorIdsAsUnique);
mProviders.add(provider);
if (DEBUG) {
Log.d(TAG, "Provider added: " + provider);
@@ -3339,8 +3396,11 @@
// possible for there to be two providers with the same package name.
// Therefore we must dedupe the composite id.
String componentName = provider.getComponentName().flattenToShortString();
- String uniqueId = componentName + ":" + routeDescriptorId;
- if (findRouteByUniqueId(uniqueId) < 0) {
+ String uniqueId =
+ provider.mTreatRouteDescriptorIdsAsUnique
+ ? routeDescriptorId
+ : (componentName + ":" + routeDescriptorId);
+ if (provider.mTreatRouteDescriptorIdsAsUnique || findRouteByUniqueId(uniqueId) < 0) {
mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId);
return uniqueId;
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
index 5514799..8cdbb96 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
@@ -21,8 +21,21 @@
import static android.media.MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK;
import static android.media.MediaRoute2Info.FEATURE_REMOTE_PLAYBACK;
import static android.media.MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_AUDIO_VIDEO_RECEIVER;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_CAR;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_COMPUTER;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_GAME_CONSOLE;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_GROUP;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_SMARTWATCH;
import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_TABLET;
+import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_TABLET_DOCKED;
import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_TV;
import static androidx.mediarouter.media.MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN;
@@ -35,9 +48,12 @@
import android.text.TextUtils;
import android.util.ArraySet;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.mediarouter.media.MediaRouter.RouteInfo;
import java.util.ArrayList;
import java.util.Collection;
@@ -63,8 +79,20 @@
static final String KEY_SESSION_NAME = "androidx.mediarouter.media.KEY_SESSION_NAME";
static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE";
+ // TODO(b/282263784): Remove the following constants in favor of using instead SDK constants,
+ // once the SDK constants become available.
+ private static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003;
+ private static final int TYPE_REMOTE_TABLET = 1004;
+ private static final int TYPE_REMOTE_TABLET_DOCKED = 1005;
+ private static final int TYPE_REMOTE_COMPUTER = 1006;
+ private static final int TYPE_REMOTE_GAME_CONSOLE = 1007;
+ private static final int TYPE_REMOTE_CAR = 1008;
+ private static final int TYPE_REMOTE_SMARTWATCH = 1009;
+ private static final int TYPE_GROUP = 2000;
+
private MediaRouter2Utils() {}
+ @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
@Nullable
public static MediaRoute2Info toFwkMediaRoute2Info(@Nullable MediaRouteDescriptor descriptor) {
if (descriptor == null) {
@@ -88,6 +116,13 @@
//.setClientPackageName(clientMap.get(device.getDeviceId()))
;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ Api34Impl.setDeduplicationIds(builder, descriptor.getDeduplicationIds());
+ Api34Impl.copyDescriptorVisibilityToBuilder(builder, descriptor);
+ Api34Impl.setDeviceType(
+ builder, androidXDeviceTypeToFwkDeviceType(descriptor.getDeviceType()));
+ }
+
switch (descriptor.getDeviceType()) {
case DEVICE_TYPE_TV:
builder.addFeature(FEATURE_REMOTE_VIDEO_PLAYBACK);
@@ -118,6 +153,7 @@
return builder.build();
}
+ @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
@Nullable
public static MediaRouteDescriptor toMediaRouteDescriptor(
@Nullable MediaRoute2Info fwkMediaRoute2Info) {
@@ -135,6 +171,13 @@
.setEnabled(true)
.setCanDisconnect(false);
+ @RouteInfo.DeviceType int deviceTypeInRouteInfo = DEVICE_TYPE_UNKNOWN;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ builder.setDeduplicationIds(Api34Impl.getDeduplicationIds(fwkMediaRoute2Info));
+ deviceTypeInRouteInfo =
+ fwkDeviceTypeToAndroidXDeviceType(Api34Impl.getType(fwkMediaRoute2Info));
+ }
+
CharSequence description = fwkMediaRoute2Info.getDescription();
if (description != null) {
builder.setDescription(description.toString());
@@ -154,9 +197,11 @@
}
builder.setExtras(extras.getBundle(KEY_EXTRAS));
- builder.setDeviceType(extras.getInt(KEY_DEVICE_TYPE, DEVICE_TYPE_UNKNOWN));
- builder.setPlaybackType(extras.getInt(KEY_PLAYBACK_TYPE,
- MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE));
+ builder.setDeviceType(
+ deviceTypeInRouteInfo != DEVICE_TYPE_UNKNOWN
+ ? deviceTypeInRouteInfo
+ : extras.getInt(KEY_DEVICE_TYPE, DEVICE_TYPE_UNKNOWN));
+ builder.setPlaybackType(extras.getInt(KEY_PLAYBACK_TYPE, RouteInfo.PLAYBACK_TYPE_REMOTE));
List<IntentFilter> controlFilters = extras.getParcelableArrayList(KEY_CONTROL_FILTERS);
if (controlFilters != null) {
@@ -276,4 +321,95 @@
}
return routeFeature;
}
+
+ @RouteInfo.DeviceType
+ private static int fwkDeviceTypeToAndroidXDeviceType(int fwkDeviceType) {
+ switch (fwkDeviceType) {
+ case TYPE_REMOTE_TV:
+ return DEVICE_TYPE_TV;
+ case TYPE_REMOTE_SPEAKER:
+ return DEVICE_TYPE_SPEAKER;
+ case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+ return DEVICE_TYPE_AUDIO_VIDEO_RECEIVER;
+ case TYPE_REMOTE_TABLET:
+ return DEVICE_TYPE_TABLET;
+ case TYPE_REMOTE_TABLET_DOCKED:
+ return DEVICE_TYPE_TABLET_DOCKED;
+ case TYPE_REMOTE_COMPUTER:
+ return DEVICE_TYPE_COMPUTER;
+ case TYPE_REMOTE_GAME_CONSOLE:
+ return DEVICE_TYPE_GAME_CONSOLE;
+ case TYPE_REMOTE_CAR:
+ return DEVICE_TYPE_CAR;
+ case TYPE_REMOTE_SMARTWATCH:
+ return DEVICE_TYPE_SMARTWATCH;
+ case TYPE_GROUP:
+ return DEVICE_TYPE_GROUP;
+ default:
+ return DEVICE_TYPE_UNKNOWN;
+ }
+ }
+
+ private static int androidXDeviceTypeToFwkDeviceType(
+ @RouteInfo.DeviceType int androidXDeviceType) {
+ switch (androidXDeviceType) {
+ case DEVICE_TYPE_TV:
+ return TYPE_REMOTE_TV;
+ case DEVICE_TYPE_SPEAKER:
+ return TYPE_REMOTE_SPEAKER;
+ case DEVICE_TYPE_AUDIO_VIDEO_RECEIVER:
+ return TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
+ case DEVICE_TYPE_TABLET:
+ return TYPE_REMOTE_TABLET;
+ case DEVICE_TYPE_TABLET_DOCKED:
+ return TYPE_REMOTE_TABLET_DOCKED;
+ case DEVICE_TYPE_COMPUTER:
+ return TYPE_REMOTE_COMPUTER;
+ case DEVICE_TYPE_GAME_CONSOLE:
+ return TYPE_REMOTE_GAME_CONSOLE;
+ case DEVICE_TYPE_CAR:
+ return TYPE_REMOTE_CAR;
+ case DEVICE_TYPE_SMARTWATCH:
+ return TYPE_REMOTE_SMARTWATCH;
+ case DEVICE_TYPE_GROUP:
+ return TYPE_GROUP;
+ default:
+ return TYPE_UNKNOWN;
+ }
+ }
+
+ @RequiresApi(api = 34)
+ private static final class Api34Impl {
+
+ @DoNotInline
+ public static void setDeduplicationIds(
+ MediaRoute2Info.Builder builder, Set<String> deduplicationIds) {
+ builder.setDeduplicationIds(deduplicationIds);
+ }
+
+ @DoNotInline
+ public static Set<String> getDeduplicationIds(MediaRoute2Info fwkMediaRoute2Info) {
+ return fwkMediaRoute2Info.getDeduplicationIds();
+ }
+
+ @DoNotInline
+ public static void copyDescriptorVisibilityToBuilder(MediaRoute2Info.Builder builder,
+ MediaRouteDescriptor descriptor) {
+ if (descriptor.isVisibilityPublic()) {
+ builder.setVisibilityPublic();
+ } else {
+ builder.setVisibilityRestricted(descriptor.getAllowedPackages());
+ }
+ }
+
+ @DoNotInline
+ public static void setDeviceType(MediaRoute2Info.Builder builder, int deviceType) {
+ builder.setType(deviceType);
+ }
+
+ @DoNotInline
+ public static int getType(MediaRoute2Info fwkMediaRoute2Info) {
+ return fwkMediaRoute2Info.getType();
+ }
+ }
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java
new file mode 100644
index 0000000..3be72e6
--- /dev/null
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.mediarouter.media;
+
+import android.annotation.SuppressLint;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.text.TextUtils;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Allows applications to customize the list of routes used for media routing (for example, in the
+ * System UI Output Switcher).
+ *
+ * @see MediaRouter#setRouteListingPreference
+ * @see RouteListingPreference.Item
+ */
+public final class RouteListingPreference {
+
+ /**
+ * {@link Intent} action that the system uses to take the user the app when the user selects an
+ * {@link RouteListingPreference.Item} whose {@link
+ * RouteListingPreference.Item#getSelectionBehavior() selection behavior} is {@link
+ * RouteListingPreference.Item#SELECTION_BEHAVIOR_GO_TO_APP}.
+ *
+ * <p>The launched intent will identify the selected item using the extra identified by {@link
+ * #EXTRA_ROUTE_ID}.
+ *
+ * @see #getLinkedItemComponentName()
+ * @see RouteListingPreference.Item#SELECTION_BEHAVIOR_GO_TO_APP
+ */
+ @SuppressLint("ActionValue") // Field & value copied from android.media.RouteListingPreference.
+ public static final String ACTION_TRANSFER_MEDIA =
+ android.media.RouteListingPreference.ACTION_TRANSFER_MEDIA;
+
+ /**
+ * {@link Intent} string extra key that contains the {@link
+ * RouteListingPreference.Item#getRouteId() id} of the route to transfer to, as part of an
+ * {@link #ACTION_TRANSFER_MEDIA} intent.
+ *
+ * @see #getLinkedItemComponentName()
+ * @see RouteListingPreference.Item#SELECTION_BEHAVIOR_GO_TO_APP
+ */
+ @SuppressLint("ActionValue") // Field & value copied from android.media.RouteListingPreference.
+ public static final String EXTRA_ROUTE_ID = android.media.RouteListingPreference.EXTRA_ROUTE_ID;
+
+ @NonNull private final List<RouteListingPreference.Item> mItems;
+ private final boolean mUseSystemOrdering;
+ @Nullable private final ComponentName mLinkedItemComponentName;
+
+ // Must be package private to avoid a synthetic accessor for the builder.
+ /* package */ RouteListingPreference(RouteListingPreference.Builder builder) {
+ mItems = builder.mItems;
+ mUseSystemOrdering = builder.mUseSystemOrdering;
+ mLinkedItemComponentName = builder.mLinkedItemComponentName;
+ }
+
+ /**
+ * Returns an unmodifiable list containing the {@link RouteListingPreference.Item items} that
+ * the app wants to be listed for media routing.
+ */
+ @NonNull
+ public List<RouteListingPreference.Item> getItems() {
+ return mItems;
+ }
+
+ /**
+ * Returns true if the application would like media route listing to use the system's ordering
+ * strategy, or false if the application would like route listing to respect the ordering
+ * obtained from {@link #getItems()}.
+ *
+ * <p>The system's ordering strategy is implementation-dependent, but may take into account each
+ * route's recency or frequency of use in order to rank them.
+ */
+ public boolean getUseSystemOrdering() {
+ return mUseSystemOrdering;
+ }
+
+ /**
+ * Returns a {@link ComponentName} for navigating to the application.
+ *
+ * <p>Must not be null if any of the {@link #getItems() items} of this route listing preference
+ * has {@link RouteListingPreference.Item#getSelectionBehavior() selection behavior} {@link
+ * RouteListingPreference.Item#SELECTION_BEHAVIOR_GO_TO_APP}.
+ *
+ * <p>The system navigates to the application when the user selects {@link
+ * RouteListingPreference.Item} with {@link
+ * RouteListingPreference.Item#SELECTION_BEHAVIOR_GO_TO_APP} by launching an intent to the
+ * returned {@link ComponentName}, using action {@link #ACTION_TRANSFER_MEDIA}, with the extra
+ * {@link #EXTRA_ROUTE_ID}.
+ */
+ @Nullable
+ public ComponentName getLinkedItemComponentName() {
+ return mLinkedItemComponentName;
+ }
+
+ // Equals and hashCode.
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof RouteListingPreference)) {
+ return false;
+ }
+ RouteListingPreference that = (RouteListingPreference) other;
+ return mItems.equals(that.mItems)
+ && mUseSystemOrdering == that.mUseSystemOrdering
+ && Objects.equals(mLinkedItemComponentName, that.mLinkedItemComponentName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mItems, mUseSystemOrdering, mLinkedItemComponentName);
+ }
+
+ // Internal methods.
+
+ /** @hide */
+ @RequiresApi(api = 34)
+ @NonNull /* package */
+ android.media.RouteListingPreference toPlatformRouteListingPreference() {
+ return Api34Impl.toPlatformRouteListingPreference(this);
+ }
+
+ // Inner classes.
+
+ /** Builder for {@link RouteListingPreference}. */
+ public static final class Builder {
+
+ // The builder fields must be package private to avoid synthetic accessors.
+ /* package */ List<RouteListingPreference.Item> mItems;
+ /* package */ boolean mUseSystemOrdering;
+ /* package */ ComponentName mLinkedItemComponentName;
+
+ /** Creates a new instance with default values (documented in the setters). */
+ public Builder() {
+ mItems = Collections.emptyList();
+ mUseSystemOrdering = true;
+ }
+
+ /**
+ * See {@link #getItems()}
+ *
+ * <p>The default value is an empty list.
+ */
+ @NonNull
+ public RouteListingPreference.Builder setItems(
+ @NonNull List<RouteListingPreference.Item> items) {
+ mItems = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(items)));
+ return this;
+ }
+
+ /**
+ * See {@link #getUseSystemOrdering()}
+ *
+ * <p>The default value is {@code true}.
+ */
+ // Lint requires "isUseSystemOrdering", but "getUseSystemOrdering" is a better name.
+ @SuppressWarnings("MissingGetterMatchingBuilder")
+ @NonNull
+ public RouteListingPreference.Builder setUseSystemOrdering(boolean useSystemOrdering) {
+ mUseSystemOrdering = useSystemOrdering;
+ return this;
+ }
+
+ /**
+ * See {@link #getLinkedItemComponentName()}.
+ *
+ * <p>The default value is {@code null}.
+ */
+ @NonNull
+ public RouteListingPreference.Builder setLinkedItemComponentName(
+ @Nullable ComponentName linkedItemComponentName) {
+ mLinkedItemComponentName = linkedItemComponentName;
+ return this;
+ }
+
+ /**
+ * Creates and returns a new {@link RouteListingPreference} instance with the given
+ * parameters.
+ */
+ @NonNull
+ public RouteListingPreference build() {
+ return new RouteListingPreference(this);
+ }
+ }
+
+ /** Holds preference information for a specific route in a {@link RouteListingPreference}. */
+ public static final class Item {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ value = {
+ SELECTION_BEHAVIOR_NONE,
+ SELECTION_BEHAVIOR_TRANSFER,
+ SELECTION_BEHAVIOR_GO_TO_APP
+ })
+ public @interface SelectionBehavior {}
+
+ /** The corresponding route is not selectable by the user. */
+ public static final int SELECTION_BEHAVIOR_NONE = 0;
+ /** If the user selects the corresponding route, the media transfers to the said route. */
+ public static final int SELECTION_BEHAVIOR_TRANSFER = 1;
+ /**
+ * If the user selects the corresponding route, the system takes the user to the
+ * application.
+ *
+ * <p>The system uses {@link #getLinkedItemComponentName()} in order to navigate to the app.
+ */
+ public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ value = {FLAG_ONGOING_SESSION, FLAG_ONGOING_SESSION_MANAGED, FLAG_SUGGESTED})
+ public @interface Flags {}
+
+ /**
+ * The corresponding route is already hosting a session with the app that owns this listing
+ * preference.
+ */
+ public static final int FLAG_ONGOING_SESSION = 1;
+
+ /**
+ * Signals that the ongoing session on the corresponding route is managed by the current
+ * user of the app.
+ *
+ * <p>The system can use this flag to provide visual indication that the route is not only
+ * hosting a session, but also that the user has ownership over said session.
+ *
+ * <p>This flag is ignored if {@link #FLAG_ONGOING_SESSION} is not set, or if the
+ * corresponding route is not currently selected.
+ *
+ * <p>This flag does not affect volume adjustment (see {@link
+ * androidx.media.VolumeProviderCompat}, and {@link
+ * MediaRouteDescriptor#getVolumeHandling()}), or any aspect other than the visual
+ * representation of the corresponding item.
+ */
+ public static final int FLAG_ONGOING_SESSION_MANAGED = 1 << 1;
+
+ /**
+ * The corresponding route is specially likely to be selected by the user.
+ *
+ * <p>A UI reflecting this preference may reserve a specific space for suggested routes,
+ * making it more accessible to the user. If the number of suggested routes exceeds the
+ * number supported by the UI, the routes listed first in {@link
+ * RouteListingPreference#getItems()} will take priority.
+ */
+ public static final int FLAG_SUGGESTED = 1 << 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ value = {
+ SUBTEXT_NONE,
+ SUBTEXT_ERROR_UNKNOWN,
+ SUBTEXT_SUBSCRIPTION_REQUIRED,
+ SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED,
+ SUBTEXT_AD_ROUTING_DISALLOWED,
+ SUBTEXT_DEVICE_LOW_POWER,
+ SUBTEXT_UNAUTHORIZED,
+ SUBTEXT_TRACK_UNSUPPORTED,
+ SUBTEXT_CUSTOM
+ })
+ public @interface SubText {}
+
+ /** The corresponding route has no associated subtext. */
+ public static final int SUBTEXT_NONE =
+ android.media.RouteListingPreference.Item.SUBTEXT_NONE;
+ /**
+ * The corresponding route's subtext must indicate that it is not available because of an
+ * unknown error.
+ */
+ public static final int SUBTEXT_ERROR_UNKNOWN =
+ android.media.RouteListingPreference.Item.SUBTEXT_ERROR_UNKNOWN;
+ /**
+ * The corresponding route's subtext must indicate that it requires a special subscription
+ * in order to be available for routing.
+ */
+ public static final int SUBTEXT_SUBSCRIPTION_REQUIRED =
+ android.media.RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED;
+ /**
+ * The corresponding route's subtext must indicate that downloaded content cannot be routed
+ * to it.
+ */
+ public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED =
+ android.media.RouteListingPreference.Item
+ .SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED;
+ /**
+ * The corresponding route's subtext must indicate that it is not available because an ad is
+ * in progress.
+ */
+ public static final int SUBTEXT_AD_ROUTING_DISALLOWED =
+ android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
+ /**
+ * The corresponding route's subtext must indicate that it is not available because the
+ * device is in low-power mode.
+ */
+ public static final int SUBTEXT_DEVICE_LOW_POWER =
+ android.media.RouteListingPreference.Item.SUBTEXT_DEVICE_LOW_POWER;
+ /**
+ * The corresponding route's subtext must indicate that it is not available because the user
+ * is not authorized to route to it.
+ */
+ public static final int SUBTEXT_UNAUTHORIZED =
+ android.media.RouteListingPreference.Item.SUBTEXT_UNAUTHORIZED;
+ /**
+ * The corresponding route's subtext must indicate that it is not available because the
+ * device does not support the current media track.
+ */
+ public static final int SUBTEXT_TRACK_UNSUPPORTED =
+ android.media.RouteListingPreference.Item.SUBTEXT_TRACK_UNSUPPORTED;
+ /**
+ * The corresponding route's subtext must be obtained from {@link
+ * #getCustomSubtextMessage()}.
+ *
+ * <p>Applications should strongly prefer one of the other disable reasons (for the full
+ * list, see {@link #getSubText()}) in order to guarantee correct localization and rendering
+ * across all form factors.
+ */
+ public static final int SUBTEXT_CUSTOM =
+ android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM;
+
+ @NonNull private final String mRouteId;
+ @SelectionBehavior private final int mSelectionBehavior;
+ @Flags private final int mFlags;
+ @SubText private final int mSubText;
+
+ @Nullable private final CharSequence mCustomSubtextMessage;
+
+ // Must be package private to avoid a synthetic accessor for the builder.
+ /* package */ Item(@NonNull RouteListingPreference.Item.Builder builder) {
+ mRouteId = builder.mRouteId;
+ mSelectionBehavior = builder.mSelectionBehavior;
+ mFlags = builder.mFlags;
+ mSubText = builder.mSubText;
+ mCustomSubtextMessage = builder.mCustomSubtextMessage;
+ validateCustomMessageSubtext();
+ }
+
+ /**
+ * Returns the id of the route that corresponds to this route listing preference item.
+ *
+ * @see MediaRouter.RouteInfo#getId()
+ */
+ @NonNull
+ public String getRouteId() {
+ return mRouteId;
+ }
+
+ /**
+ * Returns the behavior that the corresponding route has if the user selects it.
+ *
+ * @see #SELECTION_BEHAVIOR_NONE
+ * @see #SELECTION_BEHAVIOR_TRANSFER
+ * @see #SELECTION_BEHAVIOR_GO_TO_APP
+ */
+ public int getSelectionBehavior() {
+ return mSelectionBehavior;
+ }
+
+ /**
+ * Returns the flags associated to the route that corresponds to this item.
+ *
+ * @see #FLAG_ONGOING_SESSION
+ * @see #FLAG_ONGOING_SESSION_MANAGED
+ * @see #FLAG_SUGGESTED
+ */
+ @Flags
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns the type of subtext associated to this route.
+ *
+ * <p>Subtext types other than {@link #SUBTEXT_NONE} and {@link #SUBTEXT_CUSTOM} must not
+ * have {@link #SELECTION_BEHAVIOR_TRANSFER}.
+ *
+ * <p>If this method returns {@link #SUBTEXT_CUSTOM}, then the subtext is obtained form
+ * {@link #getCustomSubtextMessage()}.
+ *
+ * @see #SUBTEXT_NONE
+ * @see #SUBTEXT_ERROR_UNKNOWN
+ * @see #SUBTEXT_SUBSCRIPTION_REQUIRED
+ * @see #SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED
+ * @see #SUBTEXT_AD_ROUTING_DISALLOWED
+ * @see #SUBTEXT_DEVICE_LOW_POWER
+ * @see #SUBTEXT_UNAUTHORIZED
+ * @see #SUBTEXT_TRACK_UNSUPPORTED
+ * @see #SUBTEXT_CUSTOM
+ */
+ @SubText
+ public int getSubText() {
+ return mSubText;
+ }
+
+ /**
+ * Returns a human-readable {@link CharSequence} providing the subtext for the corresponding
+ * route.
+ *
+ * <p>This value is ignored if the {@link #getSubText() subtext} for this item is not {@link
+ * #SUBTEXT_CUSTOM}..
+ *
+ * <p>Applications must provide a localized message that matches the system's locale. See
+ * {@link Locale#getDefault()}.
+ *
+ * <p>Applications should avoid using custom messages (and instead use one of non-custom
+ * subtexts listed in {@link #getSubText()} in order to guarantee correct visual
+ * representation and localization on all form factors.
+ */
+ @Nullable
+ public CharSequence getCustomSubtextMessage() {
+ return mCustomSubtextMessage;
+ }
+
+ // Equals and hashCode.
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof RouteListingPreference.Item)) {
+ return false;
+ }
+ RouteListingPreference.Item item = (RouteListingPreference.Item) other;
+ return mRouteId.equals(item.mRouteId)
+ && mSelectionBehavior == item.mSelectionBehavior
+ && mFlags == item.mFlags
+ && mSubText == item.mSubText
+ && TextUtils.equals(mCustomSubtextMessage, item.mCustomSubtextMessage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mRouteId, mSelectionBehavior, mFlags, mSubText, mCustomSubtextMessage);
+ }
+
+ // Internal methods.
+
+ private void validateCustomMessageSubtext() {
+ Preconditions.checkArgument(
+ mSubText != SUBTEXT_CUSTOM || mCustomSubtextMessage != null,
+ "The custom subtext message cannot be null if subtext is SUBTEXT_CUSTOM.");
+ }
+
+ // Internal classes.
+
+ /** Builder for {@link RouteListingPreference.Item}. */
+ public static final class Builder {
+
+ // The builder fields must be package private to avoid synthetic accessors.
+ /* package */ final String mRouteId;
+ /* package */ int mSelectionBehavior;
+ /* package */ int mFlags;
+ /* package */ int mSubText;
+ /* package */ CharSequence mCustomSubtextMessage;
+
+ /**
+ * Constructor.
+ *
+ * @param routeId See {@link RouteListingPreference.Item#getRouteId()}.
+ */
+ public Builder(@NonNull String routeId) {
+ Preconditions.checkArgument(!TextUtils.isEmpty(routeId));
+ mRouteId = routeId;
+ mSelectionBehavior = SELECTION_BEHAVIOR_TRANSFER;
+ mSubText = SUBTEXT_NONE;
+ }
+
+ /**
+ * See {@link RouteListingPreference.Item#getSelectionBehavior()}.
+ *
+ * <p>The default value is {@link #ACTION_TRANSFER_MEDIA}.
+ */
+ @NonNull
+ public RouteListingPreference.Item.Builder setSelectionBehavior(int selectionBehavior) {
+ mSelectionBehavior = selectionBehavior;
+ return this;
+ }
+
+ /**
+ * See {@link RouteListingPreference.Item#getFlags()}.
+ *
+ * <p>The default value is zero (no flags).
+ */
+ @NonNull
+ public RouteListingPreference.Item.Builder setFlags(int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * See {@link RouteListingPreference.Item#getSubText()}.
+ *
+ * <p>The default value is {@link #SUBTEXT_NONE}.
+ */
+ @NonNull
+ public RouteListingPreference.Item.Builder setSubText(int subText) {
+ mSubText = subText;
+ return this;
+ }
+
+ /**
+ * See {@link RouteListingPreference.Item#getCustomSubtextMessage()}.
+ *
+ * <p>The default value is {@code null}.
+ */
+ @NonNull
+ public RouteListingPreference.Item.Builder setCustomSubtextMessage(
+ @Nullable CharSequence customSubtextMessage) {
+ mCustomSubtextMessage = customSubtextMessage;
+ return this;
+ }
+
+ /**
+ * Creates and returns a new {@link RouteListingPreference.Item} with the given
+ * parameters.
+ */
+ @NonNull
+ public RouteListingPreference.Item build() {
+ return new RouteListingPreference.Item(this);
+ }
+ }
+ }
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ private Api34Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ @NonNull
+ public static android.media.RouteListingPreference toPlatformRouteListingPreference(
+ RouteListingPreference routeListingPreference) {
+ ArrayList<android.media.RouteListingPreference.Item> platformRlpItems =
+ new ArrayList<>();
+ for (Item item : routeListingPreference.getItems()) {
+ platformRlpItems.add(toPlatformItem(item));
+ }
+
+ return new android.media.RouteListingPreference.Builder()
+ .setItems(platformRlpItems)
+ .setLinkedItemComponentName(routeListingPreference.getLinkedItemComponentName())
+ .setUseSystemOrdering(routeListingPreference.getUseSystemOrdering())
+ .build();
+ }
+
+ @DoNotInline
+ @NonNull
+ public static android.media.RouteListingPreference.Item toPlatformItem(Item item) {
+ return new android.media.RouteListingPreference.Item.Builder(item.getRouteId())
+ .setFlags(item.getFlags())
+ .setSubText(item.getSubText())
+ .setCustomSubtextMessage(item.getCustomSubtextMessage())
+ .setSelectionBehavior(item.getSelectionBehavior())
+ .build();
+ }
+ }
+}
diff --git a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseTestActivity.java b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseTestActivity.java
index 790793b..9dcf237 100755
--- a/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseTestActivity.java
+++ b/percentlayout/percentlayout/src/androidTest/java/androidx/percentlayout/widget/BaseTestActivity.java
@@ -22,6 +22,7 @@
abstract class BaseTestActivity extends Activity {
@Override
+ @SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(0, 0);
@@ -34,6 +35,7 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
+ @SuppressWarnings("deprecation")
@Override
public void finish() {
super.finish();
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
index b6155a8..da35abe 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java
@@ -85,8 +85,8 @@
@Test
public void testTopicsManager_runClassifier() throws Exception {
- // Skip the test if SDK extension 4 is not present.
- Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4);
+ // Skip the test if SDK extension 5 is not present.
+ Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
TopicsManagerFutures topicsManager =
TopicsManagerFutures.from(ApplicationProvider.getApplicationContext());
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 30cd307..839dec6 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -100,13 +100,20 @@
package androidx.privacysandbox.ads.adservices.common {
public final class AdData {
- ctor public AdData(android.net.Uri renderUri, String metadata);
+ ctor public AdData(optional android.net.Uri renderUri, optional String metadata);
method public String getMetadata();
method public android.net.Uri getRenderUri();
property public final String metadata;
property public final android.net.Uri renderUri;
}
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class AdData.Builder {
+ ctor public AdData.Builder();
+ method public androidx.privacysandbox.ads.adservices.common.AdData build();
+ method public androidx.privacysandbox.ads.adservices.common.AdData.Builder setMetadata(String metadata);
+ method public androidx.privacysandbox.ads.adservices.common.AdData.Builder setRenderUri(android.net.Uri renderUri);
+ }
+
public final class AdSelectionSignals {
ctor public AdSelectionSignals(String signals);
method public String getSignals();
@@ -185,13 +192,20 @@
}
public final class TrustedBiddingData {
- ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ ctor public TrustedBiddingData(optional android.net.Uri trustedBiddingUri, optional java.util.List<java.lang.String> trustedBiddingKeys);
method public java.util.List<java.lang.String> getTrustedBiddingKeys();
method public android.net.Uri getTrustedBiddingUri();
property public final java.util.List<java.lang.String> trustedBiddingKeys;
property public final android.net.Uri trustedBiddingUri;
}
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class TrustedBiddingData.Builder {
+ ctor public TrustedBiddingData.Builder();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData.Builder setTrustedBiddingKeys(java.util.List<java.lang.String> trustedBiddingKeys);
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData.Builder setTrustedBiddingUri(android.net.Uri trustedBiddingUri);
+ }
+
}
package androidx.privacysandbox.ads.adservices.measurement {
diff --git a/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta05.txt b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta05.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta05.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ public abstract class AdSelectionManager {
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class ReportImpressionRequest {
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean getShouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 30cd307..839dec6 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -100,13 +100,20 @@
package androidx.privacysandbox.ads.adservices.common {
public final class AdData {
- ctor public AdData(android.net.Uri renderUri, String metadata);
+ ctor public AdData(optional android.net.Uri renderUri, optional String metadata);
method public String getMetadata();
method public android.net.Uri getRenderUri();
property public final String metadata;
property public final android.net.Uri renderUri;
}
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class AdData.Builder {
+ ctor public AdData.Builder();
+ method public androidx.privacysandbox.ads.adservices.common.AdData build();
+ method public androidx.privacysandbox.ads.adservices.common.AdData.Builder setMetadata(String metadata);
+ method public androidx.privacysandbox.ads.adservices.common.AdData.Builder setRenderUri(android.net.Uri renderUri);
+ }
+
public final class AdSelectionSignals {
ctor public AdSelectionSignals(String signals);
method public String getSignals();
@@ -185,13 +192,20 @@
}
public final class TrustedBiddingData {
- ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ ctor public TrustedBiddingData(optional android.net.Uri trustedBiddingUri, optional java.util.List<java.lang.String> trustedBiddingKeys);
method public java.util.List<java.lang.String> getTrustedBiddingKeys();
method public android.net.Uri getTrustedBiddingUri();
property public final java.util.List<java.lang.String> trustedBiddingKeys;
property public final android.net.Uri trustedBiddingUri;
}
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class TrustedBiddingData.Builder {
+ ctor public TrustedBiddingData.Builder();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData.Builder setTrustedBiddingKeys(java.util.List<java.lang.String> trustedBiddingKeys);
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData.Builder setTrustedBiddingUri(android.net.Uri trustedBiddingUri);
+ }
+
}
package androidx.privacysandbox.ads.adservices.measurement {
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/common/AdDataTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/common/AdDataTest.kt
index 501b15f..c548abb 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/common/AdDataTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/common/AdDataTest.kt
@@ -17,7 +17,10 @@
package androidx.privacysandbox.ads.adservices.common
import android.net.Uri
+import android.os.Build
+import androidx.annotation.RequiresApi
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth
import org.junit.Test
@@ -25,6 +28,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 33)
class AdDataTest {
private val uri: Uri = Uri.parse("abc.com")
private val metadata = "metadata"
@@ -41,4 +45,14 @@
var adData2 = AdData(Uri.parse("abc.com"), "metadata")
Truth.assertThat(adData1 == adData2).isTrue()
}
+
+ @Test
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ fun testBuilderSetters() {
+ val constructed = AdData(uri, metadata)
+ val builder = AdData.Builder()
+ .setRenderUri(uri)
+ .setMetadata(metadata)
+ Truth.assertThat(builder.build()).isEqualTo(constructed)
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingDataTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingDataTest.kt
index 1476dae..d26ffe5 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingDataTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingDataTest.kt
@@ -17,7 +17,10 @@
package androidx.privacysandbox.ads.adservices.customaudience
import android.net.Uri
+import android.os.Build
+import androidx.annotation.RequiresApi
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth
import org.junit.Test
@@ -25,6 +28,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 33)
class TrustedBiddingDataTest {
private val uri = Uri.parse("abc.com")
private val keys = listOf("key1", "key2")
@@ -34,4 +38,14 @@
val trustedBiddingData = TrustedBiddingData(uri, keys)
Truth.assertThat(trustedBiddingData.toString()).isEqualTo(result)
}
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ @Test
+ fun testBuilderSetters() {
+ val constructed = TrustedBiddingData(uri, keys)
+ val builder = TrustedBiddingData.Builder()
+ .setTrustedBiddingUri(uri)
+ .setTrustedBiddingKeys(keys)
+ Truth.assertThat(builder.build()).isEqualTo(constructed)
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
index ec458ba..7685d0b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
@@ -17,6 +17,8 @@
package androidx.privacysandbox.ads.adservices.common
import android.net.Uri
+import android.os.Build
+import androidx.annotation.RequiresApi
/**
* Represents data specific to an ad that is necessary for ad selection and rendering.
@@ -24,8 +26,8 @@
* @param metadata buyer ad metadata represented as a JSON string
*/
class AdData public constructor(
- val renderUri: Uri,
- val metadata: String
+ val renderUri: Uri = Uri.EMPTY,
+ val metadata: String = ""
) {
/** Checks whether two [AdData] objects contain the same information. */
@@ -47,4 +49,42 @@
override fun toString(): String {
return "AdData: renderUri=$renderUri, metadata='$metadata'"
}
+
+ /** Builder for [AdData] objects. */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public class Builder {
+ private var renderUri: Uri = Uri.EMPTY
+ private var metadata: String = ""
+
+ /**
+ * Sets the URI that points to the ad's rendering assets. The URI must use HTTPS.
+ *
+ * @param renderUri a URI pointing to the ad's rendering assets
+ */
+ fun setRenderUri(renderUri: Uri): Builder = apply {
+ this.renderUri = renderUri
+ }
+
+ /**
+ * Sets the buyer ad metadata used during the ad selection process.
+ *
+ * @param metadata The metadata should be a valid JSON object serialized as a string.
+ * Metadata represents ad-specific bidding information that will be used during ad selection
+ * as part of bid generation and used in buyer JavaScript logic, which is executed in an
+ * isolated execution environment.
+ *
+ * If the metadata is not a valid JSON object that can be consumed by the buyer's JS, the
+ * ad will not be eligible for ad selection.
+ */
+ fun setMetadata(metadata: String): Builder = apply {
+ this.metadata = metadata
+ }
+
+ /**
+ * Builds an instance of [AdData]
+ */
+ fun build(): AdData {
+ return AdData(renderUri, metadata)
+ }
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingData.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingData.kt
index fef0a18..86b4672 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingData.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/TrustedBiddingData.kt
@@ -17,6 +17,8 @@
package androidx.privacysandbox.ads.adservices.customaudience
import android.net.Uri
+import android.os.Build
+import androidx.annotation.RequiresApi
/**
* Represents data used during the ad selection process to fetch buyer bidding signals from a
@@ -29,8 +31,8 @@
* bidding signals.
*/
class TrustedBiddingData public constructor(
- val trustedBiddingUri: Uri,
- val trustedBiddingKeys: List<String>
+ val trustedBiddingUri: Uri = Uri.EMPTY,
+ val trustedBiddingKeys: List<String> = emptyList()
) {
/**
* @return `true` if two [TrustedBiddingData] objects contain the same information
@@ -53,4 +55,38 @@
return "TrustedBiddingData: trustedBiddingUri=$trustedBiddingUri " +
"trustedBiddingKeys=$trustedBiddingKeys"
}
+
+ /** Builder for [TrustedBiddingData] objects. */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public class Builder {
+ private var trustedBiddingUri: Uri = Uri.EMPTY
+ private var trustedBiddingKeys: List<String> = emptyList()
+
+ /**
+ * Sets the trusted Bidding Uri
+ *
+ * @param trustedBiddingUri the URI pointing to the trusted key-value server holding bidding
+ * signals. The URI must use HTTPS.
+ */
+ fun setTrustedBiddingUri(trustedBiddingUri: Uri): Builder = apply {
+ this.trustedBiddingUri = trustedBiddingUri
+ }
+
+ /**
+ * Sets the trusted Bidding keys.
+ *
+ * @param trustedBiddingKeys list of keys to query the trusted key-value server with.
+ * This list is permitted to be empty.
+ */
+ fun setTrustedBiddingKeys(trustedBiddingKeys: List<String>): Builder = apply {
+ this.trustedBiddingKeys = trustedBiddingKeys
+ }
+
+ /**
+ * Builds and instance of [TrustedBiddingData]
+ */
+ fun build(): TrustedBiddingData {
+ return TrustedBiddingData(trustedBiddingUri, trustedBiddingKeys)
+ }
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore b/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore
index 725751e..b3df653 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore
@@ -1,4 +1,8 @@
// Baseline format: 1.0
+ForbiddenSuperClass: androidx.privacysandbox.sdkruntime.client.activity.SdkActivity:
+ SdkActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
+
+
RegistrationName: androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat#addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat):
Callback methods should be named register/unregister; was addSdkSandboxProcessDeathCallback
RegistrationName: androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat#removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat):
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt b/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
index 8d97c00..4251e2e 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
@@ -4,10 +4,14 @@
public final class SdkSandboxManagerCompat {
method public void addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor callbackExecutor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
method public static androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+ method public java.util.List<androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat> getAppOwnedSdkSandboxInterfaces();
method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public suspend Object? loadSdk(String sdkName, android.os.Bundle params, kotlin.coroutines.Continuation<? super androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat>) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+ method public void registerAppOwnedSdkSandboxInterface(androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat appOwnedSdk);
method public void removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+ method public void startSdkSandboxActivity(android.app.Activity fromActivity, android.os.IBinder sdkActivityToken);
method public void unloadSdk(String sdkName);
+ method public void unregisterAppOwnedSdkSandboxInterface(String sdkName);
field public static final androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat.Companion Companion;
}
@@ -21,3 +25,11 @@
}
+package androidx.privacysandbox.sdkruntime.client.activity {
+
+ public final class SdkActivity extends androidx.activity.ComponentActivity implements androidx.lifecycle.LifecycleOwner {
+ ctor public SdkActivity();
+ }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt b/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
index 8d97c00..588ade4 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
@@ -4,10 +4,14 @@
public final class SdkSandboxManagerCompat {
method public void addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor callbackExecutor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
method public static androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+ method public java.util.List<androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat> getAppOwnedSdkSandboxInterfaces();
method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public suspend Object? loadSdk(String sdkName, android.os.Bundle params, kotlin.coroutines.Continuation<? super androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat>) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+ method public void registerAppOwnedSdkSandboxInterface(androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat appOwnedSdk);
method public void removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+ method public void startSdkSandboxActivity(android.app.Activity fromActivity, android.os.IBinder sdkActivityToken);
method public void unloadSdk(String sdkName);
+ method public void unregisterAppOwnedSdkSandboxInterface(String sdkName);
field public static final androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat.Companion Companion;
}
@@ -21,3 +25,11 @@
}
+package androidx.privacysandbox.sdkruntime.client.activity {
+
+ public final class SdkActivity extends androidx.activity.ComponentActivity {
+ ctor public SdkActivity();
+ }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index a0f618d..486fe73 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -25,11 +25,12 @@
dependencies {
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesCore)
- implementation("androidx.core:core-ktx:1.8.0")
+ implementation("androidx.core:core-ktx:1.12.0-alpha03")
api project(path: ':privacysandbox:sdkruntime:sdkruntime-core')
- implementation("androidx.core:core:1.8.0")
+ implementation("androidx.core:core:1.12.0-alpha03")
+ implementation(projectOrArtifact(":activity:activity"))
testImplementation(libs.junit)
testImplementation(libs.truth)
@@ -42,6 +43,7 @@
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.junit)
+ androidTestImplementation(project(':internal-testutils-runtime'))
androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
index 7b70e67..59ae122 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
@@ -1,23 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
-
- <issue
- id="NewApi"
- message="Call requires API level 33 (current min is 14): `android.app.sdksandbox.SdkSandboxManager#getSandboxedSdks`"
- errorLine1=" `when`(sdkSandboxManager.sandboxedSdks)"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 33 (current min is 14): `android.app.sdksandbox.SdkSandboxManager#getSandboxedSdks`"
- errorLine1=" `when`(sdkSandboxManager.sandboxedSdks)"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt"/>
- </issue>
+<issues format="6" by="lint 8.1.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta01)" variant="all" version="8.1.0-beta01">
<issue
id="BanThreadSleep"
@@ -46,4 +28,13 @@
file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt"/>
</issue>
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" return if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt"/>
+ </issue>
+
</issues>
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml
index 3e3ea90..98769ac 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml
@@ -14,4 +14,14 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <activity
+ android:name="androidx.privacysandbox.sdkruntime.client.EmptyActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
index c72c1a2..3ac54e2 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
@@ -24,6 +24,10 @@
<package-name>androidx.privacysandbox.sdkruntime.test.v2</package-name>
</runtime-enabled-sdk>
<runtime-enabled-sdk>
+ <compat-config-path>RuntimeEnabledSdks/V3/CompatSdkConfig.xml</compat-config-path>
+ <package-name>androidx.privacysandbox.sdkruntime.test.v3</package-name>
+ </runtime-enabled-sdk>
+ <runtime-enabled-sdk>
<compat-config-path>RuntimeEnabledSdks/InvalidEntryPointSdkConfig.xml</compat-config-path>
<package-name>androidx.privacysandbox.sdkruntime.test.invalidEntryPoint</package-name>
</runtime-enabled-sdk>
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkCode.md b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkCode.md
new file mode 100644
index 0000000..750e2a3
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkCode.md
@@ -0,0 +1,263 @@
+Test sdk that was built with V3 library.
+
+DO NOT RECOMPILE WITH ANY CHANGES TO LIBRARY CLASSES.
+Main purpose of that provider is to test that old core versions could be loaded by new client.
+
+classes.dex built from:
+
+1) androidx.privacysandbox.sdkruntime.core.Versions
+@Keep
+object Versions {
+
+ const val API_VERSION = 3
+
+ @JvmField
+ var CLIENT_VERSION: Int? = null
+
+ @JvmStatic
+ fun handShake(clientVersion: Int): Int {
+ CLIENT_VERSION = clientVersion
+ return API_VERSION
+ }
+}
+
+2) androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+abstract class SandboxedSdkProviderCompat {
+ var context: Context? = null
+ private set
+
+ fun attachContext(context: Context) {
+ check(this.context == null) { "Context already set" }
+ this.context = context
+ }
+
+ @Throws(LoadSdkCompatException::class)
+ abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+
+ open fun beforeUnloadSdk() {}
+
+ abstract fun getView(
+ windowContext: Context,
+ params: Bundle,
+ width: Int,
+ height: Int
+ ): View
+}
+
+3) androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+class SandboxedSdkCompat private constructor(
+ private val sdkImpl: SandboxedSdkImpl
+) {
+
+ constructor(sdkInterface: IBinder) : this(sdkInterface, sdkInfo = null)
+
+ @Keep
+ constructor(
+ sdkInterface: IBinder,
+ sdkInfo: SandboxedSdkInfo?
+ ) : this(CompatImpl(sdkInterface, sdkInfo))
+
+ fun getInterface() = sdkImpl.getInterface()
+
+ fun getSdkInfo(): SandboxedSdkInfo? = sdkImpl.getSdkInfo()
+
+ internal interface SandboxedSdkImpl {
+ fun getInterface(): IBinder?
+ fun getSdkInfo(): SandboxedSdkInfo?
+ }
+
+ private class CompatImpl(
+ private val sdkInterface: IBinder,
+ private val sdkInfo: SandboxedSdkInfo?
+ ) : SandboxedSdkImpl {
+
+ override fun getInterface(): IBinder {
+ return sdkInterface
+ }
+
+ override fun getSdkInfo(): SandboxedSdkInfo? = sdkInfo
+ }
+}
+
+4) androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+class LoadSdkCompatException : Exception {
+
+ val loadSdkErrorCode: Int
+
+ val extraInformation: Bundle
+
+ @JvmOverloads
+ constructor(
+ loadSdkErrorCode: Int,
+ message: String?,
+ cause: Throwable?,
+ extraInformation: Bundle = Bundle()
+ ) : super(message, cause) {
+ this.loadSdkErrorCode = loadSdkErrorCode
+ this.extraInformation = extraInformation
+ }
+
+ constructor(
+ cause: Throwable,
+ extraInfo: Bundle
+ ) : this(LOAD_SDK_SDK_DEFINED_ERROR, "", cause, extraInfo)
+
+ companion object {
+ const val LOAD_SDK_SDK_DEFINED_ERROR = 102
+ }
+}
+
+5) androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+class SandboxedSdkInfo(
+ val name: String,
+ val version: Long
+) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+ other as SandboxedSdkInfo
+ if (name != other.name) return false
+ if (version != other.version) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = name.hashCode()
+ result = 31 * result + version.hashCode()
+ return result
+ }
+}
+
+6) androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+interface ActivityHolder : LifecycleOwner {
+ fun getActivity(): Activity
+ fun getOnBackPressedDispatcher(): OnBackPressedDispatcher
+}
+
+7) androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+interface SdkSandboxActivityHandlerCompat {
+ fun onActivityCreated(activityHolder: ActivityHolder)
+}
+
+8) androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+class SdkSandboxControllerCompat internal constructor(
+ private val controllerImpl: SandboxControllerImpl
+) {
+ fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+ controllerImpl.getSandboxedSdks()
+
+ fun registerSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat):
+ IBinder = controllerImpl.registerSdkSandboxActivityHandler(handlerCompat)
+
+ fun unregisterSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat) =
+ controllerImpl.unregisterSdkSandboxActivityHandler(handlerCompat)
+
+ interface SandboxControllerImpl {
+ fun getSandboxedSdks(): List<SandboxedSdkCompat>
+ fun registerSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat):
+ IBinder
+ fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ )
+ }
+
+ companion object {
+ private var localImpl: SandboxControllerImpl? = null
+ @JvmStatic
+ fun from(context: Context): SdkSandboxControllerCompat {
+ val clientVersion = Versions.CLIENT_VERSION
+ if (clientVersion != null) {
+ val implFromClient = localImpl
+ if (implFromClient != null) {
+ return SdkSandboxControllerCompat(LocalImpl(implFromClient, clientVersion))
+ }
+ }
+ throw IllegalStateException("Should be loaded locally")
+ }
+ @JvmStatic
+ @Keep
+ fun injectLocalImpl(impl: SandboxControllerImpl) {
+ check(localImpl == null) { "Local implementation already injected" }
+ localImpl = impl
+ }
+ }
+}
+
+9) androidx.privacysandbox.sdkruntime.core.controller.impl.LocalImpl
+internal class LocalImpl(
+ private val implFromClient: SdkSandboxControllerCompat.SandboxControllerImpl,
+ private val clientVersion: Int
+) : SdkSandboxControllerCompat.SandboxControllerImpl {
+ override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+ return implFromClient.getSandboxedSdks()
+ }
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ if (clientVersion < 3) {
+ throw UnsupportedOperationException(
+ "Client library version doesn't support SdkActivities"
+ )
+ }
+ return implFromClient.registerSdkSandboxActivityHandler(handlerCompat)
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ if (clientVersion < 3) {
+ throw UnsupportedOperationException(
+ "Client library version doesn't support SdkActivities"
+ )
+ }
+ implFromClient.unregisterSdkSandboxActivityHandler(handlerCompat)
+ }
+}
+
+10) androidx.privacysandbox.sdkruntime.test.v3.CompatProvider
+class CompatProvider : SandboxedSdkProviderCompat() {
+
+ @JvmField
+ var onLoadSdkBinder: Binder? = null
+
+ @JvmField
+ var lastOnLoadSdkParams: Bundle? = null
+
+ @JvmField
+ var isBeforeUnloadSdkCalled = false
+
+ @Throws(LoadSdkCompatException::class)
+ override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+ val result = SdkImpl(context!!)
+ onLoadSdkBinder = result
+ if (params.getBoolean("needFail", false)) {
+ throw LoadSdkCompatException(RuntimeException(), params)
+ }
+ return SandboxedSdkCompat(result)
+ }
+
+ override fun beforeUnloadSdk() {
+ isBeforeUnloadSdkCalled = true
+ }
+
+ override fun getView(
+ windowContext: Context, params: Bundle, width: Int,
+ height: Int
+ ): View {
+ return View(windowContext)
+ }
+
+ class SdkImpl(
+ private val context: Context
+ ) : Binder() {
+ fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+ SdkSandboxControllerCompat.from(context).getSandboxedSdks()
+ fun registerSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat): IBinder =
+ SdkSandboxControllerCompat.from(context).registerSdkSandboxActivityHandler(handler)
+ fun unregisterSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat) {
+ SdkSandboxControllerCompat.from(context).unregisterSdkSandboxActivityHandler(handler)
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkConfig.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkConfig.xml
new file mode 100644
index 0000000..5c23d99
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/CompatSdkConfig.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<compat-config>
+ <compat-entrypoint>androidx.privacysandbox.sdkruntime.test.v3.CompatProvider</compat-entrypoint>
+ <dex-path>RuntimeEnabledSdks/V3/classes.dex</dex-path>
+</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/classes.dex b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/classes.dex
new file mode 100644
index 0000000..9740bdf
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V3/classes.dex
Binary files differ
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/EmptyActivity.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/EmptyActivity.kt
new file mode 100644
index 0000000..f7d19b4
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/EmptyActivity.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client
+
+import androidx.activity.ComponentActivity
+
+/**
+ * Activity for components tests.
+ * [androidx.privacysandbox.sdkruntime.client.activity.SdkActivity] can't be used for most tests as
+ * it will be moved to finished state during creation when no valid token provided.
+ */
+class EmptyActivity : ComponentActivity()
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerAppOwnedInterfacesTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerAppOwnedInterfacesTest.kt
new file mode 100644
index 0000000..cfb4613
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerAppOwnedInterfacesTest.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client
+
+import android.app.sdksandbox.SdkSandboxManager
+import android.content.Context
+import android.os.Binder
+import android.os.Build
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.RequiresExtension
+import androidx.core.content.getSystemService
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedInterfaceConverter
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+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 org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+// TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+@RequiresExtension(extension = AD_SERVICES, version = 4)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+class SdkSandboxManagerAppOwnedInterfacesTest {
+
+ private lateinit var mContext: Context
+
+ @Before
+ fun setUp() {
+ assumeTrue("Requires Sandbox API available", AdServicesInfo.isAtLeastV4())
+ mContext = ApplicationProvider.getApplicationContext()
+ }
+
+ @After
+ fun tearDown() {
+ SdkSandboxManagerCompat.reset()
+ if (isAppOwnedInterfacesApiAvailable()) {
+ val registeredInterfaces = getRegisteredInterfaces()
+ unregisterInterfaces(registeredInterfaces)
+ }
+ }
+
+ @Test
+ fun registerAppOwnedSdkSandboxInterface_whenApiNotAvailable_throwsException() {
+ assumeFalse(
+ "Requires AppOwnedInterfacesApi API not available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+ assertThrows(UnsupportedOperationException::class.java) {
+ managerCompat.registerAppOwnedSdkSandboxInterface(
+ AppOwnedSdkSandboxInterfaceCompat(
+ name = "TestSDK",
+ version = 1,
+ binder = Binder()
+ )
+ )
+ }
+ }
+
+ @Test
+ fun registerAppOwnedSdkSandboxInterface_whenApiAvailable_delegateToPlatform() {
+ assumeTrue(
+ "Requires AppOwnedInterfacesApi API available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+ val appOwnedInterface = AppOwnedSdkSandboxInterfaceCompat(
+ name = "TestSDK",
+ version = 1,
+ binder = Binder()
+ )
+
+ managerCompat.registerAppOwnedSdkSandboxInterface(appOwnedInterface)
+
+ val registeredInterfaces = getRegisteredInterfaces()
+ assertThat(registeredInterfaces).hasSize(1)
+ val result = registeredInterfaces[0]
+
+ assertThat(result.getName()).isEqualTo(appOwnedInterface.getName())
+ assertThat(result.getVersion()).isEqualTo(appOwnedInterface.getVersion())
+ assertThat(result.getInterface()).isEqualTo(appOwnedInterface.getInterface())
+ }
+
+ @Test
+ fun unregisterAppOwnedSdkSandboxInterface_whenApiNotAvailable_throwsException() {
+ assumeFalse(
+ "Requires AppOwnedInterfacesApi API not available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+ assertThrows(UnsupportedOperationException::class.java) {
+ managerCompat.unregisterAppOwnedSdkSandboxInterface("TestSDK")
+ }
+ }
+
+ @Test
+ fun unregisterAppOwnedSdkSandboxInterface_whenApiAvailable_delegateToPlatform() {
+ assumeTrue(
+ "Requires AppOwnedInterfacesApi API available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+ val appOwnedInterface = AppOwnedSdkSandboxInterfaceCompat(
+ name = "TestSDK",
+ version = 1,
+ binder = Binder()
+ )
+
+ managerCompat.registerAppOwnedSdkSandboxInterface(appOwnedInterface)
+ managerCompat.unregisterAppOwnedSdkSandboxInterface(appOwnedInterface.getName())
+
+ val registeredInterfaces = getRegisteredInterfaces()
+ assertThat(registeredInterfaces).isEmpty()
+ }
+
+ @Test
+ fun getAppOwnedSdkSandboxInterfaces_whenApiNotAvailable_throwsException() {
+ assumeFalse(
+ "Requires AppOwnedInterfacesApi API not available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+ assertThrows(UnsupportedOperationException::class.java) {
+ managerCompat.getAppOwnedSdkSandboxInterfaces()
+ }
+ }
+
+ @Test
+ fun getAppOwnedSdkSandboxInterfaces_whenApiAvailable_delegateToPlatform() {
+ assumeTrue(
+ "Requires AppOwnedInterfacesApi API available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+ val appOwnedInterface = AppOwnedSdkSandboxInterfaceCompat(
+ name = "TestSDK",
+ version = 1,
+ binder = Binder()
+ )
+ managerCompat.registerAppOwnedSdkSandboxInterface(appOwnedInterface)
+
+ val results = managerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(results).hasSize(1)
+ val result = results[0]
+
+ assertThat(result.getName()).isEqualTo(appOwnedInterface.getName())
+ assertThat(result.getVersion()).isEqualTo(appOwnedInterface.getVersion())
+ assertThat(result.getInterface()).isEqualTo(appOwnedInterface.getInterface())
+ }
+
+ private fun getRegisteredInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ val converter = AppOwnedInterfaceConverter()
+
+ val sandboxManager = mContext.getSystemService<SdkSandboxManager>()!!
+ val getInterfacesMethod = sandboxManager.javaClass.getMethod(
+ "getAppOwnedSdkSandboxInterfaces"
+ )
+
+ val results = getInterfacesMethod.invoke(sandboxManager) as List<*>
+ return results.map { converter.toCompat(it!!) }
+ }
+
+ private fun unregisterInterfaces(appOwnedInterfaces: List<AppOwnedSdkSandboxInterfaceCompat>) {
+ val sandboxManager = mContext.getSystemService<SdkSandboxManager>()!!
+ val unregisterMethod = sandboxManager.javaClass.getMethod(
+ "unregisterAppOwnedSdkSandboxInterface",
+ /* parameter1 */ String::class.java
+ )
+
+ appOwnedInterfaces.forEach { unregisterMethod.invoke(sandboxManager, it.getName()) }
+ }
+
+ private fun isAppOwnedInterfacesApiAvailable() =
+ AdServicesInfo.isDeveloperPreview()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
index ab6b951..a56a801 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
@@ -16,6 +16,7 @@
package androidx.privacysandbox.sdkruntime.client
+import android.app.Activity
import android.app.sdksandbox.LoadSdkException
import android.app.sdksandbox.SandboxedSdk
import android.app.sdksandbox.SdkSandboxManager
@@ -23,6 +24,7 @@
import android.os.Binder
import android.os.Build
import android.os.Bundle
+import android.os.IBinder
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions.AD_SERVICES
import androidx.annotation.RequiresExtension
@@ -169,6 +171,19 @@
}
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ fun startSdkSandboxActivity_whenSandboxAvailable_delegateToPlatform() {
+ val sdkSandboxManager = mockSandboxManager(mContext)
+ val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+ val fromActivityMock = mock(Activity::class.java)
+ val tokenMock = mock(IBinder::class.java)
+ managerCompat.startSdkSandboxActivity(fromActivityMock, tokenMock)
+
+ verify(sdkSandboxManager).startSdkSandboxActivity(fromActivityMock, tokenMock)
+ }
+
+ @Test
fun removeSdkSandboxProcessDeathCallback_whenSandboxAvailable_removeAddedCallback() {
val sdkSandboxManager = mockSandboxManager(mContext)
val managerCompat = SdkSandboxManagerCompat.from(mContext)
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
index 3a49aa7..8db037e 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
@@ -15,10 +15,14 @@
*/
package androidx.privacysandbox.sdkruntime.client
+import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
+import android.os.Binder
import android.os.Build
import android.os.Bundle
+import androidx.privacysandbox.sdkruntime.client.activity.SdkActivity
+import androidx.privacysandbox.sdkruntime.client.loader.CatchingSdkActivityHandler
import androidx.privacysandbox.sdkruntime.client.loader.asTestSdk
import androidx.privacysandbox.sdkruntime.client.loader.extractSdkProviderFieldValue
import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
@@ -26,16 +30,17 @@
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_INTERNAL_ERROR
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_SDK_DEFINED_ERROR
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Assume.assumeTrue
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@@ -283,7 +288,46 @@
verify(context, Mockito.never()).getSystemService(any())
}
- @Ignore("b/277764220")
+ @Test
+ // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ fun startSdkSandboxActivity_whenSandboxNotAvailable_dontDelegateToSandbox() {
+ // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+ assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+ val context = spy(ApplicationProvider.getApplicationContext<Context>())
+ val managerCompat = SdkSandboxManagerCompat.from(context)
+
+ val fromActivitySpy = Mockito.mock(Activity::class.java)
+ managerCompat.startSdkSandboxActivity(fromActivitySpy, Binder())
+
+ verify(context, Mockito.never()).getSystemService(any())
+ }
+
+ @Test
+ fun startSdkSandboxActivity_startLocalSdkActivity() {
+ val context = ApplicationProvider.getApplicationContext<Context>()
+ val managerCompat = SdkSandboxManagerCompat.from(context)
+
+ val localSdk = runBlocking {
+ managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v3", Bundle())
+ }
+
+ val handler = CatchingSdkActivityHandler()
+
+ val testSdk = localSdk.asTestSdk()
+ val token = testSdk.registerSdkSandboxActivityHandler(handler)
+
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ managerCompat.startSdkSandboxActivity(this, token)
+ }
+ }
+
+ val activityHolder = handler.waitForActivity()
+ assertThat(activityHolder.getActivity()).isInstanceOf(SdkActivity::class.java)
+ }
+
@Test
fun sdkController_getSandboxedSdks_returnsLocallyLoadedSdks() {
val context = ApplicationProvider.getApplicationContext<Context>()
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/TestActivityHolder.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/TestActivityHolder.kt
new file mode 100644
index 0000000..66a8470
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/TestActivityHolder.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client
+
+import android.app.Activity
+import androidx.activity.OnBackPressedDispatcher
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+
+internal class TestActivityHolder(
+ private val activity: Activity,
+) : ActivityHolder {
+ val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
+ val backPresseddispatcher: OnBackPressedDispatcher = OnBackPressedDispatcher()
+
+ override fun getActivity(): Activity = activity
+
+ override fun getOnBackPressedDispatcher(): OnBackPressedDispatcher = backPresseddispatcher
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarterTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarterTest.kt
new file mode 100644
index 0000000..e418460
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarterTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.activity
+
+import android.os.Binder
+import androidx.privacysandbox.sdkruntime.client.EmptyActivity
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import androidx.test.core.app.ActivityScenario
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import org.junit.Test
+
+class LocalSdkActivityStarterTest {
+
+ @Test
+ fun tryStart_whenHandlerRegistered_startSdkActivityAndReturnTrue() {
+ val handler = TestHandler()
+ val registeredToken = LocalSdkActivityHandlerRegistry.register(handler)
+
+ val startResult = with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ LocalSdkActivityStarter.tryStart(this, registeredToken)
+ }
+ }
+
+ assertThat(startResult).isTrue()
+
+ val activityHolder = handler.waitForActivity()
+ assertThat(activityHolder.getActivity()).isInstanceOf(SdkActivity::class.java)
+
+ val sdkActivity = activityHolder.getActivity() as SdkActivity
+ assertThat(activityHolder.lifecycle).isSameInstanceAs(sdkActivity.lifecycle)
+ assertThat(activityHolder.getOnBackPressedDispatcher())
+ .isSameInstanceAs(sdkActivity.onBackPressedDispatcher)
+ }
+
+ @Test
+ fun tryStart_whenHandlerNotRegistered_ReturnFalse() {
+ val unregisteredToken = Binder()
+
+ val startResult = with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ LocalSdkActivityStarter.tryStart(this, unregisteredToken)
+ }
+ }
+
+ assertThat(startResult).isFalse()
+ }
+
+ private class TestHandler : SdkSandboxActivityHandlerCompat {
+ var result: ActivityHolder? = null
+ var async = CountDownLatch(1)
+
+ override fun onActivityCreated(activityHolder: ActivityHolder) {
+ result = activityHolder
+ async.countDown()
+ }
+
+ fun waitForActivity(): ActivityHolder {
+ async.await()
+ return result!!
+ }
+ }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
index b44ba98..cf99675 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
@@ -18,8 +18,11 @@
import android.os.Binder
import android.os.Bundle
+import androidx.privacysandbox.sdkruntime.client.activity.LocalSdkActivityHandlerRegistry
import androidx.privacysandbox.sdkruntime.client.loader.LocalSdkProvider
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -54,6 +57,35 @@
assertThat(result).containsExactly(sandboxedSdk)
}
+ @Test
+ fun registerSdkSandboxActivityHandler_delegateToLocalSdkActivityHandlerRegistry() {
+ val handler = object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {
+ // do nothing
+ }
+ }
+
+ val token = controller.registerSdkSandboxActivityHandler(handler)
+
+ val registeredHandler = LocalSdkActivityHandlerRegistry.getHandlerByToken(token)
+ assertThat(registeredHandler).isSameInstanceAs(handler)
+ }
+
+ @Test
+ fun unregisterSdkSandboxActivityHandler_delegateToLocalSdkActivityHandlerRegistry() {
+ val handler = object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {
+ // do nothing
+ }
+ }
+
+ val token = controller.registerSdkSandboxActivityHandler(handler)
+ controller.unregisterSdkSandboxActivityHandler(handler)
+
+ val registeredHandler = LocalSdkActivityHandlerRegistry.getHandlerByToken(token)
+ assertThat(registeredHandler).isNull()
+ }
+
private class NoOpSdkProvider : LocalSdkProvider(Any()) {
override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
throw IllegalStateException("Unexpected call")
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
index f34de7b1..532e773 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
@@ -18,19 +18,27 @@
import android.content.Context
import android.os.Binder
import android.os.Bundle
+import android.os.IBinder
import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.privacysandbox.sdkruntime.client.EmptyActivity
+import androidx.privacysandbox.sdkruntime.client.TestActivityHolder
import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
import androidx.privacysandbox.sdkruntime.client.loader.impl.SandboxedSdkContextCompat
import androidx.privacysandbox.sdkruntime.client.loader.storage.TestLocalSdkStorage
import androidx.privacysandbox.sdkruntime.client.loader.storage.toClassPathString
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
import com.google.common.truth.Truth.assertThat
import dalvik.system.BaseDexClassLoader
import java.io.File
@@ -124,6 +132,75 @@
assertThat(result.getSdkVersion()).isEqualTo(expectedResult.getSdkInfo()!!.version)
}
+ @Test
+ fun registerSdkSandboxActivityHandler_delegateToSdkController() {
+ assumeTrue(
+ "Requires Versions.API_VERSION >= 3",
+ sdkVersion >= 3
+ )
+
+ val catchingHandler = CatchingSdkActivityHandler()
+
+ val testSdk = loadedSdk.loadTestSdk()
+ val token = testSdk.registerSdkSandboxActivityHandler(catchingHandler)
+ val localHandler = controller.sdkActivityHandlers[token]!!
+
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ localHandler.onActivityCreated(activityHolder)
+
+ val receivedActivityHolder = catchingHandler.result!!
+ val receivedActivity = receivedActivityHolder.getActivity()
+ assertThat(receivedActivity).isSameInstanceAs(activityHolder.getActivity())
+ }
+ }
+ }
+
+ @Test
+ fun sdkSandboxActivityHandler_ReceivesLifecycleEventsFromOriginalActivityHolder() {
+ assumeTrue(
+ "Requires Versions.API_VERSION >= 3",
+ sdkVersion >= 3
+ )
+
+ val catchingHandler = CatchingSdkActivityHandler()
+
+ val testSdk = loadedSdk.loadTestSdk()
+ val token = testSdk.registerSdkSandboxActivityHandler(catchingHandler)
+ val localHandler = controller.sdkActivityHandlers[token]!!
+
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ localHandler.onActivityCreated(activityHolder)
+ val receivedActivityHolder = catchingHandler.result!!
+
+ for (event in Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_ANY }) {
+ activityHolder.lifecycleRegistry.handleLifecycleEvent(event)
+ assertThat(receivedActivityHolder.getLifeCycleCurrentState())
+ .isEqualTo(event.targetState)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun unregisterSdkSandboxActivityHandler_delegateToSdkController() {
+ assumeTrue(
+ "Requires Versions.API_VERSION >= 3",
+ sdkVersion >= 3
+ )
+
+ val handler = CatchingSdkActivityHandler()
+
+ val testSdk = loadedSdk.loadTestSdk()
+ val token = testSdk.registerSdkSandboxActivityHandler(handler)
+ testSdk.unregisterSdkSandboxActivityHandler(handler)
+
+ assertThat(controller.sdkActivityHandlers[token]).isNull()
+ }
+
class CurrentVersionProviderLoadTest : SandboxedSdkProviderCompat() {
@JvmField
var onLoadSdkBinder: Binder? = null
@@ -166,6 +243,13 @@
) : Binder() {
fun getSandboxedSdks(): List<SandboxedSdkCompat> =
SdkSandboxControllerCompat.from(context).getSandboxedSdks()
+
+ fun registerSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat): IBinder =
+ SdkSandboxControllerCompat.from(context).registerSdkSandboxActivityHandler(handler)
+
+ fun unregisterSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat) {
+ SdkSandboxControllerCompat.from(context).unregisterSdkSandboxActivityHandler(handler)
+ }
}
internal class TestClassLoaderFactory(
@@ -214,6 +298,11 @@
2,
"RuntimeEnabledSdks/V2/classes.dex",
"androidx.privacysandbox.sdkruntime.test.v2.CompatProvider"
+ ),
+ TestSdkInfo(
+ 3,
+ "RuntimeEnabledSdks/V3/classes.dex",
+ "androidx.privacysandbox.sdkruntime.test.v3.CompatProvider"
)
)
@@ -222,14 +311,12 @@
fun params(): List<Array<Any>> = buildList {
assertThat(SDKS.size).isEqualTo(Versions.API_VERSION)
- val controller = TestStubController()
-
- val assetsSdkLoader = createAssetsSdkLoader(controller)
for (i in SDKS.indices) {
val sdk = SDKS[i]
assertThat(sdk.apiVersion).isEqualTo(i + 1)
- val loadedSdk = assetsSdkLoader.loadSdk(sdk.localSdkConfig)
+ val controller = TestStubController()
+ val loadedSdk = loadTestSdkFromAssets(sdk.localSdkConfig, controller)
assertThat(loadedSdk.extractApiVersion())
.isEqualTo(sdk.apiVersion)
@@ -244,6 +331,7 @@
}
// add SDK loaded from test sources
+ val controller = TestStubController()
add(
arrayOf(
"BuiltFromSource",
@@ -275,26 +363,50 @@
)
}
- private fun createAssetsSdkLoader(controller: TestStubController): SdkLoader {
+ private fun loadTestSdkFromAssets(
+ sdkConfig: LocalSdkConfig,
+ controller: TestStubController
+ ): LocalSdkProvider {
val context = ApplicationProvider.getApplicationContext<Context>()
val testStorage = TestLocalSdkStorage(
context,
rootFolder = File(context.cacheDir, "LocalSdkTest")
)
- return SdkLoader(
+ val sdkLoader = SdkLoader(
TestClassLoaderFactory(testStorage),
context,
controller
)
+ return sdkLoader.loadSdk(sdkConfig)
}
}
internal class TestStubController : SdkSandboxControllerCompat.SandboxControllerImpl {
var sandboxedSdksResult: List<SandboxedSdkCompat> = emptyList()
+ var sdkActivityHandlers: MutableMap<IBinder, SdkSandboxActivityHandlerCompat> =
+ mutableMapOf()
override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
return sandboxedSdksResult
}
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ throw IllegalStateException("Should not be called in this test")
+ }
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ val token = Binder()
+ sdkActivityHandlers[token] = handlerCompat
+ return token
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ sdkActivityHandlers.values.remove(handlerCompat)
+ }
}
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
index 40aee3a..44e1ce4 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
@@ -16,12 +16,17 @@
package androidx.privacysandbox.sdkruntime.client.loader
+import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.os.IBinder
+import androidx.lifecycle.Lifecycle
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import java.lang.reflect.Proxy
+import java.util.concurrent.CountDownLatch
import kotlin.reflect.cast
/**
@@ -72,6 +77,8 @@
* Underlying TestSDK should implement and delegate to
* [androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat]:
* 1) getSandboxedSdks() : List<SandboxedSdkCompat>
+ * 2) registerSdkSandboxActivityHandler(SdkSandboxActivityHandlerCompat) : IBinder
+ * 3) unregisterSdkSandboxActivityHandler(SdkSandboxActivityHandlerCompat)
*/
internal class TestSdkWrapper(
private val sdk: Any
@@ -82,6 +89,54 @@
) as List<*>
return sdks.map { SandboxedSdkWrapper(it!!) }
}
+
+ fun registerSdkSandboxActivityHandler(handler: CatchingSdkActivityHandler): IBinder {
+ val classLoader = sdk.javaClass.classLoader!!
+ val activityHandlerClass = Class.forName(
+ SdkSandboxActivityHandlerCompat::class.java.name,
+ false,
+ classLoader
+ )
+
+ val proxy = Proxy.newProxyInstance(
+ classLoader,
+ arrayOf(activityHandlerClass)
+ ) { proxy, method, args ->
+ when (method.name) {
+ "hashCode" -> hashCode()
+ "equals" -> proxy === args[0]
+ "onActivityCreated" -> handler.setResult(args[0])
+ else -> {
+ throw UnsupportedOperationException(
+ "Unexpected method call object:$proxy, method: $method, args: $args"
+ )
+ }
+ }
+ }
+
+ val registerMethod = sdk.javaClass
+ .getMethod("registerSdkSandboxActivityHandler", activityHandlerClass)
+
+ val token = registerMethod.invoke(sdk, proxy) as IBinder
+ handler.proxy = proxy
+
+ return token
+ }
+
+ fun unregisterSdkSandboxActivityHandler(handler: CatchingSdkActivityHandler) {
+ val classLoader = sdk.javaClass.classLoader!!
+ val activityHandlerClass = Class.forName(
+ SdkSandboxActivityHandlerCompat::class.java.name,
+ false,
+ classLoader
+ )
+
+ val unregisterMethod = sdk.javaClass
+ .getMethod("unregisterSdkSandboxActivityHandler", activityHandlerClass)
+
+ unregisterMethod.invoke(sdk, handler.proxy)
+ handler.proxy = null
+ }
}
/**
@@ -124,6 +179,52 @@
}
/**
+ * ActivityHandler to use with [TestSdkWrapper.registerSdkSandboxActivityHandler].
+ * Store received ActivityHolder.
+ */
+internal class CatchingSdkActivityHandler {
+ var proxy: Any? = null
+ var result: ActivityHolderWrapper? = null
+ val async = CountDownLatch(1)
+
+ fun waitForActivity(): ActivityHolderWrapper {
+ async.await()
+ return result!!
+ }
+}
+
+private fun CatchingSdkActivityHandler.setResult(activityHolder: Any) {
+ result = ActivityHolderWrapper(activityHolder)
+ async.countDown()
+}
+
+/**
+ * Reflection wrapper for [androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder]
+ */
+internal class ActivityHolderWrapper(
+ private val activityHolder: Any
+) {
+ fun getActivity(): Activity {
+ return activityHolder.callMethod(
+ methodName = "getActivity"
+ ) as Activity
+ }
+
+ fun getLifeCycleCurrentState(): Lifecycle.State {
+ val lifecycle = activityHolder.callMethod(
+ methodName = "getLifecycle"
+ )
+ val currentState = lifecycle!!.callMethod(
+ methodName = "getCurrentState"
+ )
+ val currentStateString = currentState!!.callMethod(
+ methodName = "name"
+ ) as String
+ return Lifecycle.State.valueOf(currentStateString)
+ }
+}
+
+/**
* Load SDK and wrap it as TestSDK.
* @see [TestSdkWrapper]
*/
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
index 341d08f..57db8c7 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
@@ -17,11 +17,14 @@
import android.content.Context
import android.os.Build
+import android.os.IBinder
import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
import androidx.privacysandbox.sdkruntime.client.config.ResourceRemappingConfig
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -149,5 +152,21 @@
override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
throw UnsupportedOperationException("NoOp")
}
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ throw UnsupportedOperationException("NoOp")
+ }
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ throw UnsupportedOperationException("NoOp")
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ throw UnsupportedOperationException("NoOp")
+ }
}
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactoryTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactoryTest.kt
new file mode 100644
index 0000000..4aad685
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactoryTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import androidx.activity.OnBackPressedCallback
+import androidx.lifecycle.Lifecycle
+import androidx.privacysandbox.sdkruntime.client.EmptyActivity
+import androidx.privacysandbox.sdkruntime.client.TestActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.test.core.app.ActivityScenario
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+class ActivityHolderProxyFactoryTest {
+
+ private lateinit var factory: ActivityHolderProxyFactory
+
+ @Before
+ fun setUp() {
+ factory = ActivityHolderProxyFactory.createFor(javaClass.classLoader!!)
+ }
+
+ @Test
+ fun createProxyFor_RetrievesActivityFromOriginalActivityHolder() {
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ val proxy = factory.createProxyFor(activityHolder) as ActivityHolder
+ assertThat(proxy.getActivity()).isSameInstanceAs(activityHolder.getActivity())
+ }
+ }
+ }
+
+ @Suppress("ReplaceCallWithBinaryOperator") // Explicitly testing equals on proxy
+ @Test
+ fun createProxyFor_CreatesProxyWithValidEqualsAndHashCode() {
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ val proxy = factory.createProxyFor(activityHolder)
+ assertThat(proxy.equals(proxy)).isTrue()
+ assertThat(proxy.hashCode()).isEqualTo(proxy.hashCode())
+ assertThat(proxy.toString()).isEqualTo(proxy.toString())
+ }
+ }
+ }
+
+ @Test
+ fun getOnBackPressedDispatcher_ProxyBackPressedFromSourceDispatcher() {
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ val proxy = factory.createProxyFor(activityHolder) as ActivityHolder
+ val callback = CountingOnBackPressedCallback()
+
+ val sourceDispatcher = activityHolder.getOnBackPressedDispatcher()
+ val proxyDispatcher = proxy.getOnBackPressedDispatcher()
+
+ proxyDispatcher.addCallback(callback)
+ sourceDispatcher.onBackPressed()
+ assertThat(callback.count).isEqualTo(1)
+
+ callback.remove()
+ sourceDispatcher.onBackPressed()
+ assertThat(callback.count).isEqualTo(1)
+ }
+ }
+ }
+
+ @Test
+ fun getOnBackPressedDispatcher_EnableSourceCallbackOnlyWhenEnabledCallbackAddedToProxy() {
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = TestActivityHolder(this)
+ val proxy = factory.createProxyFor(activityHolder) as ActivityHolder
+ val callback = CountingOnBackPressedCallback()
+
+ val sourceDispatcher = activityHolder.getOnBackPressedDispatcher()
+ val proxyDispatcher = proxy.getOnBackPressedDispatcher()
+
+ assertThat(sourceDispatcher.hasEnabledCallbacks()).isFalse()
+
+ proxyDispatcher.addCallback(callback)
+ assertThat(sourceDispatcher.hasEnabledCallbacks()).isTrue()
+
+ callback.isEnabled = false
+ assertThat(sourceDispatcher.hasEnabledCallbacks()).isFalse()
+
+ callback.isEnabled = true
+ assertThat(sourceDispatcher.hasEnabledCallbacks()).isTrue()
+
+ callback.remove()
+ assertThat(sourceDispatcher.hasEnabledCallbacks()).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun getLifecycle_ProxyLifecycleEventsFromSourceActivityHolder() {
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val sourceActivityHolder = TestActivityHolder(this)
+ val proxy = factory.createProxyFor(sourceActivityHolder) as ActivityHolder
+ for (event in Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_ANY }) {
+ sourceActivityHolder.lifecycleRegistry.handleLifecycleEvent(event)
+ assertThat(proxy.lifecycle.currentState).isEqualTo(event.targetState)
+ }
+ }
+ }
+ }
+
+ private class CountingOnBackPressedCallback : OnBackPressedCallback(true) {
+ var count = 0
+
+ override fun handleOnBackPressed() {
+ count++
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapperTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapperTest.kt
new file mode 100644
index 0000000..a4622db
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapperTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import androidx.privacysandbox.sdkruntime.client.EmptyActivity
+import androidx.privacysandbox.sdkruntime.client.activity.ComponentActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkActivityHandlerWrapperTest {
+
+ private lateinit var wrapperFactory: SdkActivityHandlerWrapper
+
+ @Before
+ fun setUp() {
+ wrapperFactory = SdkActivityHandlerWrapper.createFor(javaClass.classLoader!!)
+ }
+
+ @Test
+ fun wrapSdkSandboxActivityHandlerCompat_passActivityToOriginalHandler() {
+ val catchingHandler = TestHandler()
+
+ val wrappedHandler = wrapperFactory.wrapSdkSandboxActivityHandlerCompat(catchingHandler)
+
+ with(ActivityScenario.launch(EmptyActivity::class.java)) {
+ withActivity {
+ val activityHolder = ComponentActivityHolder(this)
+
+ wrappedHandler.onActivityCreated(activityHolder)
+ val receivedActivityHolder = catchingHandler.result!!
+
+ assertThat(receivedActivityHolder.getActivity())
+ .isSameInstanceAs(activityHolder.getActivity())
+ }
+ }
+ }
+
+ private class TestHandler : SdkSandboxActivityHandlerCompat {
+ var result: ActivityHolder? = null
+
+ override fun onActivityCreated(activityHolder: ActivityHolder) {
+ result = activityHolder
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ff1665b
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <activity
+ android:name="androidx.privacysandbox.sdkruntime.client.activity.SdkActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
index 5104e2a..9a2452f 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
@@ -16,21 +16,28 @@
package androidx.privacysandbox.sdkruntime.client
import android.annotation.SuppressLint
+import android.app.Activity
import android.app.sdksandbox.LoadSdkException
import android.app.sdksandbox.SandboxedSdk
import android.app.sdksandbox.SdkSandboxManager
import android.content.Context
import android.os.Bundle
+import android.os.IBinder
import android.os.ext.SdkExtensions.AD_SERVICES
import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresExtension
+import androidx.core.os.BuildCompat
import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.sdkruntime.client.activity.LocalSdkActivityStarter
import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfigsHolder
+import androidx.privacysandbox.sdkruntime.client.controller.AppOwnedSdkRegistry
import androidx.privacysandbox.sdkruntime.client.controller.LocalController
import androidx.privacysandbox.sdkruntime.client.controller.LocallyLoadedSdks
import androidx.privacysandbox.sdkruntime.client.loader.SdkLoader
import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_ALREADY_LOADED
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_NOT_FOUND
@@ -88,6 +95,7 @@
private val platformApi: PlatformApi,
private val configHolder: LocalSdkConfigsHolder,
private val localLocallyLoadedSdks: LocallyLoadedSdks,
+ private val appOwnedSdkRegistry: AppOwnedSdkRegistry,
private val sdkLoader: SdkLoader
) {
/**
@@ -204,6 +212,57 @@
return platformResult + localResult
}
+ /**
+ * Registers [AppOwnedSdkSandboxInterfaceCompat] for an app process.
+ *
+ * Registering an [AppOwnedSdkSandboxInterfaceCompat] that has same name as a previously
+ * registered interface will result in [IllegalStateException].
+ *
+ * [AppOwnedSdkSandboxInterfaceCompat.name] refers to the name of the interface.
+ *
+ * @param appOwnedSdk the [AppOwnedSdkSandboxInterfaceCompat] to be registered
+ */
+ fun registerAppOwnedSdkSandboxInterface(appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat) {
+ appOwnedSdkRegistry.registerAppOwnedSdkSandboxInterface(appOwnedSdk)
+ }
+
+ /**
+ * Unregisters [AppOwnedSdkSandboxInterfaceCompat] for an app process.
+ *
+ * @param sdkName the name under which [AppOwnedSdkSandboxInterfaceCompat] was registered.
+ */
+ fun unregisterAppOwnedSdkSandboxInterface(sdkName: String) {
+ appOwnedSdkRegistry.unregisterAppOwnedSdkSandboxInterface(sdkName)
+ }
+
+ /**
+ * Fetches all [AppOwnedSdkSandboxInterfaceCompat] that are registered by the app.
+ *
+ * @return List of all currently registered [AppOwnedSdkSandboxInterfaceCompat]
+ */
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ appOwnedSdkRegistry.getAppOwnedSdkSandboxInterfaces()
+
+ /**
+ * Starts an [Activity] in the SDK sandbox.
+ *
+ * This function will start a new [Activity] in the same task of the passed `fromActivity` and
+ * pass it to the SDK that shared the passed `sdkActivityToken` that identifies a request from
+ * that SDK to stat this [Activity].
+ *
+ * @param fromActivity the [Activity] will be used to start the new sandbox [Activity] by
+ * calling [Activity#startActivity] against it.
+ * @param sdkActivityToken the identifier that is shared by the SDK which requests the
+ * [Activity].
+ * @see SdkSandboxManager.startSdkSandboxActivity
+ */
+ fun startSdkSandboxActivity(fromActivity: Activity, sdkActivityToken: IBinder) {
+ if (LocalSdkActivityStarter.tryStart(fromActivity, sdkActivityToken)) {
+ return
+ }
+ platformApi.startSdkSandboxActivity(fromActivity, sdkActivityToken)
+ }
+
@TestOnly
internal fun getLocallyLoadedSdk(sdkName: String): LocallyLoadedSdks.Entry? =
localLocallyLoadedSdks.get(sdkName)
@@ -228,6 +287,8 @@
@DoNotInline
fun getSandboxedSdks(): List<SandboxedSdkCompat> = emptyList()
+
+ fun startSdkSandboxActivity(fromActivity: Activity, sdkActivityToken: IBinder)
}
@RequiresApi(33)
@@ -284,6 +345,12 @@
}
}
+ override fun startSdkSandboxActivity(fromActivity: Activity, sdkActivityToken: IBinder) {
+ throw UnsupportedOperationException(
+ "This API is only supported for devices run on Android U+"
+ )
+ }
+
private suspend fun loadSdkInternal(
sdkName: String,
params: Bundle
@@ -310,7 +377,7 @@
@RequiresApi(33)
@RequiresExtension(extension = AD_SERVICES, version = 5)
- private class ApiAdServicesV5Impl(
+ private open class ApiAdServicesV5Impl(
context: Context
) : ApiAdServicesV4Impl(context) {
@DoNotInline
@@ -321,6 +388,16 @@
}
}
+ @RequiresExtension(extension = AD_SERVICES, version = 5)
+ @RequiresApi(34)
+ private class ApiAdServicesUDCImpl(
+ context: Context
+ ) : ApiAdServicesV5Impl(context) {
+ override fun startSdkSandboxActivity(fromActivity: Activity, sdkActivityToken: IBinder) {
+ sdkSandboxManager.startSdkSandboxActivity(fromActivity, sdkActivityToken)
+ }
+ }
+
private class FailImpl : PlatformApi {
@DoNotInline
override suspend fun loadSdk(
@@ -343,6 +420,9 @@
callback: SdkSandboxProcessDeathCallbackCompat
) {
}
+
+ override fun startSdkSandboxActivity(fromActivity: Activity, sdkActivityToken: IBinder) {
+ }
}
companion object {
@@ -371,6 +451,7 @@
platformApi,
configHolder,
localSdks,
+ AppOwnedSdkRegistry.create(context),
sdkLoader
)
sInstances[context] = WeakReference(instance)
@@ -389,8 +470,11 @@
private object PlatformApiFactory {
@SuppressLint("NewApi", "ClassVerificationFailure")
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
fun create(context: Context): PlatformApi {
- return if (AdServicesInfo.isAtLeastV5()) {
+ return if (BuildCompat.isAtLeastU() || AdServicesInfo.isDeveloperPreview()) {
+ ApiAdServicesUDCImpl(context)
+ } else if (AdServicesInfo.isAtLeastV5()) {
ApiAdServicesV5Impl(context)
} else if (AdServicesInfo.isAtLeastV4()) {
ApiAdServicesV4Impl(context)
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/ComponentActivityHolder.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/ComponentActivityHolder.kt
new file mode 100644
index 0000000..f47279e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/ComponentActivityHolder.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.activity
+
+import android.app.Activity
+import androidx.activity.ComponentActivity
+import androidx.activity.OnBackPressedDispatcher
+import androidx.lifecycle.Lifecycle
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+
+/**
+ * Simple implementation of [ActivityHolder] for [ComponentActivity].
+ */
+internal class ComponentActivityHolder(
+ private val activity: ComponentActivity
+) : ActivityHolder {
+ override fun getActivity(): Activity = activity
+
+ override fun getOnBackPressedDispatcher(): OnBackPressedDispatcher =
+ activity.onBackPressedDispatcher
+
+ override val lifecycle: Lifecycle
+ get() = activity.lifecycle
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityHandlerRegistry.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityHandlerRegistry.kt
new file mode 100644
index 0000000..2f9bf53
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityHandlerRegistry.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.activity
+
+import android.os.Binder
+import android.os.IBinder
+import androidx.annotation.GuardedBy
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Singleton class to store instances of [SdkSandboxActivityHandlerCompat] registered by locally
+ * loaded SDKs.
+ */
+internal object LocalSdkActivityHandlerRegistry {
+
+ private val mapsLock = Any()
+
+ @GuardedBy("mapsLock")
+ private val handlerToToken =
+ hashMapOf<SdkSandboxActivityHandlerCompat, IBinder>()
+
+ @GuardedBy("mapsLock")
+ private val tokenToHandler =
+ hashMapOf<IBinder, SdkSandboxActivityHandlerCompat>()
+
+ fun register(handler: SdkSandboxActivityHandlerCompat): IBinder =
+ synchronized(mapsLock) {
+ val existingToken = handlerToToken[handler]
+ if (existingToken != null) {
+ return existingToken
+ }
+
+ val token = Binder()
+ handlerToToken[handler] = token
+ tokenToHandler[token] = handler
+
+ return token
+ }
+
+ fun unregister(handler: SdkSandboxActivityHandlerCompat) =
+ synchronized(mapsLock) {
+ val unregisteredToken = handlerToToken.remove(handler)
+ if (unregisteredToken != null) {
+ tokenToHandler.remove(unregisteredToken)
+ }
+ }
+
+ fun isRegistered(token: IBinder): Boolean =
+ synchronized(mapsLock) {
+ return tokenToHandler.containsKey(token)
+ }
+
+ @TestOnly
+ fun getHandlerByToken(token: IBinder): SdkSandboxActivityHandlerCompat? =
+ synchronized(mapsLock) {
+ return tokenToHandler[token]
+ }
+
+ fun notifyOnActivityCreation(
+ token: IBinder,
+ activityHolder: ActivityHolder
+ ) = synchronized(mapsLock) {
+ val handler = tokenToHandler[token]
+ ?: throw IllegalStateException("There is no registered handler to notify")
+ handler.onActivityCreated(activityHolder)
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarter.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarter.kt
new file mode 100644
index 0000000..64fc30a
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/LocalSdkActivityStarter.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.activity
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.os.IBinder
+import androidx.core.os.BundleCompat
+
+/**
+ * Singleton helper object to start [SdkActivity].
+ * Creates [Intent] with token provided by SDK.
+ */
+internal object LocalSdkActivityStarter {
+
+ private const val EXTRA_ACTIVITY_TOKEN = "androidx.privacysandbox.sdkruntime.ACTIVITY_HANDLER"
+
+ /**
+ * Trying to start [SdkActivity].
+ *
+ * If [token] registered in [LocalSdkActivityHandlerRegistry] this method will create
+ * [Intent] for starting [SdkActivity] and call [Activity.startActivity]
+ *
+ * @param fromActivity the [Activity] will be used to start the new [SdkActivity] by
+ * calling [Activity.startActivity] against it.
+ * @param token the identifier that is shared by the SDK which requests the [Activity].
+ *
+ * @return true if Intent was created, false if not (token wasn't registered locally).
+ */
+ fun tryStart(fromActivity: Activity, token: IBinder): Boolean {
+ if (!LocalSdkActivityHandlerRegistry.isRegistered(token)) {
+ return false
+ }
+
+ val intent = Intent(fromActivity, SdkActivity::class.java)
+
+ val params = Bundle()
+ BundleCompat.putBinder(params, EXTRA_ACTIVITY_TOKEN, token)
+ intent.putExtras(params)
+
+ fromActivity.startActivity(intent)
+
+ return true
+ }
+
+ /**
+ * Retrieve token from [Intent] used for creation [SdkActivity].
+ *
+ * @return token or null if [EXTRA_ACTIVITY_TOKEN] param is missing in [Intent.getExtras]
+ */
+ fun getTokenFromSdkActivityStartIntent(intent: Intent): IBinder? {
+ val params = intent.extras ?: return null
+ return BundleCompat.getBinder(params, EXTRA_ACTIVITY_TOKEN)
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/SdkActivity.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/SdkActivity.kt
new file mode 100644
index 0000000..9c25338
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/activity/SdkActivity.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.activity
+
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * Activity to start for SDKs running locally.
+ * Not for App / SDK Usage.
+ *
+ * SDK should use [SdkSandboxControllerCompat.registerSdkSandboxActivityHandler] for handler
+ * registration.
+ *
+ * App should use [SdkSandboxManagerCompat.startSdkSandboxActivity] for starting activity.
+ */
+class SdkActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ notifySdkOnActivityCreation()
+ }
+
+ private fun notifySdkOnActivityCreation() {
+ val token = LocalSdkActivityStarter.getTokenFromSdkActivityStartIntent(intent)
+ if (token == null) {
+ Log.e(
+ LOG_TAG,
+ "Token is missing in starting SdkActivity intent params"
+ )
+ finish()
+ return
+ }
+
+ try {
+ val activityHolder = ComponentActivityHolder(this)
+ LocalSdkActivityHandlerRegistry.notifyOnActivityCreation(token, activityHolder)
+ } catch (e: Exception) {
+ Log.e(
+ LOG_TAG,
+ "Failed to start the SdkActivity and going to finish it: ",
+ e
+ )
+ finish()
+ }
+ }
+
+ private companion object {
+ private const val LOG_TAG = "SdkActivity"
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/AppOwnedSdkRegistry.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/AppOwnedSdkRegistry.kt
new file mode 100644
index 0000000..c6ebf7b
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/AppOwnedSdkRegistry.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.controller
+
+import android.annotation.SuppressLint
+import android.app.sdksandbox.SdkSandboxManager
+import android.content.Context
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedInterfaceConverter
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+
+/**
+ * Register/Unregister/Fetches [AppOwnedSdkSandboxInterfaceCompat] from [SdkSandboxManager].
+ */
+internal class AppOwnedSdkRegistry private constructor(
+ private val registryImpl: RegistryImpl
+) {
+
+ fun registerAppOwnedSdkSandboxInterface(appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat) {
+ registryImpl.registerAppOwnedSdkSandboxInterface(appOwnedSdk)
+ }
+
+ fun unregisterAppOwnedSdkSandboxInterface(sdkName: String) {
+ registryImpl.unregisterAppOwnedSdkSandboxInterface(sdkName)
+ }
+
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ registryImpl.getAppOwnedSdkSandboxInterfaces()
+
+ private interface RegistryImpl {
+ fun registerAppOwnedSdkSandboxInterface(appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat)
+
+ fun unregisterAppOwnedSdkSandboxInterface(sdkName: String)
+
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat>
+ }
+
+ /**
+ * Implementation for cases when API not supported by [SdkSandboxManager]
+ * Throwing [UnsupportedOperationException] for any method calls.
+ */
+ private class FailImpl : RegistryImpl {
+ override fun registerAppOwnedSdkSandboxInterface(
+ appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat
+ ) {
+ throw UnsupportedOperationException("Requires Developer Preview 8 Build")
+ }
+
+ override fun unregisterAppOwnedSdkSandboxInterface(sdkName: String) {
+ throw UnsupportedOperationException("Requires Developer Preview 8 Build")
+ }
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ throw UnsupportedOperationException("Requires Developer Preview 8 Build")
+ }
+ }
+
+ /**
+ * Implementation for Developer Preview builds.
+ * Using reflection to call public methods / constructors.
+ * TODO(b/281397807) Replace reflection for method calls when new prebuilt will be available.
+ */
+ private class DeveloperPreviewImpl(
+ private val sdkSandboxManager: SdkSandboxManager
+ ) : RegistryImpl {
+
+ private val appOwnedInterfaceClass = Class.forName(
+ "android.app.sdksandbox.AppOwnedSdkSandboxInterface"
+ )
+
+ private val registerAppOwnedSdkMethod = sdkSandboxManager.javaClass.getMethod(
+ "registerAppOwnedSdkSandboxInterface",
+ /* parameter1 */ appOwnedInterfaceClass
+ )
+
+ private val unregisterAppOwnedSdkMethod = sdkSandboxManager.javaClass.getMethod(
+ "unregisterAppOwnedSdkSandboxInterface",
+ /* parameter1 */ String::class.java
+ )
+
+ private val getAppOwnedSdksMethod = sdkSandboxManager.javaClass.getMethod(
+ "getAppOwnedSdkSandboxInterfaces"
+ )
+
+ private val converter: AppOwnedInterfaceConverter = AppOwnedInterfaceConverter()
+
+ @SuppressLint("BanUncheckedReflection") // calling public non restricted methods
+ override fun registerAppOwnedSdkSandboxInterface(
+ appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat
+ ) {
+ val platformObj = converter.toPlatform(appOwnedSdk)
+ registerAppOwnedSdkMethod.invoke(
+ sdkSandboxManager,
+ /* parameter1 */ platformObj
+ )
+ }
+
+ @SuppressLint("BanUncheckedReflection") // calling public non restricted methods
+ override fun unregisterAppOwnedSdkSandboxInterface(sdkName: String) {
+ unregisterAppOwnedSdkMethod.invoke(
+ sdkSandboxManager,
+ /* parameter1 */ sdkName
+ )
+ }
+
+ @SuppressLint("BanUncheckedReflection") // calling public non restricted methods
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ val apiResult = getAppOwnedSdksMethod.invoke(sdkSandboxManager) as List<*>
+ return apiResult.map { converter.toCompat(it!!) }
+ }
+ }
+
+ companion object {
+ @SuppressLint("NewApi", "ClassVerificationFailure") // UpsideDownCakePrivacySandbox is UDC
+ fun create(context: Context): AppOwnedSdkRegistry {
+ return if (AdServicesInfo.isDeveloperPreview()) {
+ AppOwnedSdkRegistry(
+ DeveloperPreviewImpl(
+ context.getSystemService(
+ SdkSandboxManager::class.java
+ )
+ )
+ )
+ } else {
+ AppOwnedSdkRegistry(FailImpl())
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
index ecddba5..831a34f 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
@@ -16,7 +16,11 @@
package androidx.privacysandbox.sdkruntime.client.controller
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.client.activity.LocalSdkActivityHandlerRegistry
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
/**
@@ -29,4 +33,20 @@
override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
return locallyLoadedSdks.getLoadedSdks()
}
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ throw IllegalStateException("Should not be called")
+ }
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ return LocalSdkActivityHandlerRegistry.register(handlerCompat)
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ LocalSdkActivityHandlerRegistry.unregister(handlerCompat)
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
index 3fcefd8..f8b08ed 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
@@ -64,7 +64,7 @@
val apiVersion = VersionHandshake.perform(classLoader)
ResourceRemapping.apply(classLoader, sdkConfig.resourceRemapping)
if (apiVersion >= 2) {
- return createSdkProviderV2(classLoader, sdkConfig)
+ return createSdkProviderV2(classLoader, apiVersion, sdkConfig)
} else if (apiVersion >= 1) {
return createSdkProviderV1(classLoader, sdkConfig)
}
@@ -91,9 +91,10 @@
private fun createSdkProviderV2(
sdkClassLoader: ClassLoader,
+ sdkVersion: Int,
sdkConfig: LocalSdkConfig
): LocalSdkProvider {
- SandboxControllerInjector.inject(sdkClassLoader, controller)
+ SandboxControllerInjector.inject(sdkClassLoader, sdkVersion, controller)
return SdkProviderV1.create(sdkClassLoader, sdkConfig, appContext)
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
index 745c352..b313355 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
@@ -16,9 +16,12 @@
package androidx.privacysandbox.sdkruntime.client.loader.impl
+import android.annotation.SuppressLint
import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.client.loader.impl.injector.SdkActivityHandlerWrapper
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
import java.lang.reflect.Constructor
import java.lang.reflect.InvocationHandler
@@ -38,8 +41,10 @@
* 2) Create proxy that implements class from (1) and delegate to [controller]
* 3) Call (via reflection) [SdkSandboxControllerCompat.injectLocalImpl] with proxy from (2)
*/
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
fun inject(
sdkClassLoader: ClassLoader,
+ sdkVersion: Int,
controller: SdkSandboxControllerCompat.SandboxControllerImpl
) {
val controllerClass = Class.forName(
@@ -58,12 +63,18 @@
val sdkCompatBuilder = CompatSdkBuilder.createFor(sdkClassLoader)
+ val sdkActivityHandlerWrapper = if (sdkVersion >= 3)
+ SdkActivityHandlerWrapper.createFor(sdkClassLoader)
+ else
+ null
+
val proxy = Proxy.newProxyInstance(
sdkClassLoader,
arrayOf(controllerImplClass),
Handler(
controller,
- sdkCompatBuilder
+ sdkCompatBuilder,
+ sdkActivityHandlerWrapper
)
)
@@ -72,11 +83,23 @@
private class Handler(
private val controller: SdkSandboxControllerCompat.SandboxControllerImpl,
- private val compatSdkBuilder: CompatSdkBuilder
+ private val compatSdkBuilder: CompatSdkBuilder,
+ private val sdkActivityHandlerWrapper: SdkActivityHandlerWrapper?
) : InvocationHandler {
+
+ private val sdkToAppHandlerMap =
+ hashMapOf<Any, SdkSandboxActivityHandlerCompat>()
+
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any {
return when (method.name) {
"getSandboxedSdks" -> getSandboxedSdks()
+
+ "registerSdkSandboxActivityHandler" ->
+ registerSdkSandboxActivityHandler(args!![0]!!)
+
+ "unregisterSdkSandboxActivityHandler" ->
+ unregisterSdkSandboxActivityHandler(args!![0]!!)
+
else -> {
throw UnsupportedOperationException(
"Unexpected method call object:$proxy, method: $method, args: $args"
@@ -90,6 +113,43 @@
.getSandboxedSdks()
.map { compatSdkBuilder.createFrom(it) }
}
+
+ private fun registerSdkSandboxActivityHandler(sdkSideHandler: Any): Any {
+ val handlerToRegister = wrapSdkActivityHandler(sdkSideHandler)
+ return controller
+ .registerSdkSandboxActivityHandler(handlerToRegister)
+ }
+
+ private fun unregisterSdkSandboxActivityHandler(sdkSideHandler: Any) {
+ val appSideHandler = synchronized(sdkToAppHandlerMap) {
+ sdkToAppHandlerMap.remove(sdkSideHandler)
+ }
+ if (appSideHandler != null) {
+ controller
+ .unregisterSdkSandboxActivityHandler(appSideHandler)
+ }
+ }
+
+ private fun wrapSdkActivityHandler(sdkSideHandler: Any): SdkSandboxActivityHandlerCompat =
+ synchronized(sdkToAppHandlerMap) {
+ if (sdkActivityHandlerWrapper == null) {
+ throw IllegalStateException(
+ "Unexpected call from SDK without Activity support"
+ )
+ }
+
+ val existingAppSideHandler = sdkToAppHandlerMap[sdkSideHandler]
+ if (existingAppSideHandler != null) {
+ return existingAppSideHandler
+ }
+
+ val appSideHandler =
+ sdkActivityHandlerWrapper.wrapSdkSandboxActivityHandlerCompat(sdkSideHandler)
+
+ sdkToAppHandlerMap[sdkSideHandler] = appSideHandler
+
+ return appSideHandler
+ }
}
private class CompatSdkBuilder(
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactory.kt
new file mode 100644
index 0000000..ed9d37e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/ActivityHolderProxyFactory.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import android.app.Activity
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+
+/**
+ * Create proxy of [ActivityHolder] that implements same interface loaded by SDK Classloader.
+ */
+internal class ActivityHolderProxyFactory private constructor(
+ private val sdkClassLoader: ClassLoader,
+
+ private val activityHolderClass: Class<*>,
+
+ private val backPressedDispatcherProxyFactory: BackPressedDispatcherProxyFactory,
+
+ private val lifecycleRegistryProxyFactory: LifecycleRegistryProxyFactory,
+) {
+
+ fun createProxyFor(activityHolder: ActivityHolder): Any {
+ val dispatcherProxy = backPressedDispatcherProxyFactory.setupOnBackPressedDispatcherProxy(
+ activityHolder.getOnBackPressedDispatcher()
+ )
+
+ val handler = ActivityHolderHandler(
+ activityHolder.getActivity(),
+ dispatcherProxy
+ )
+
+ val activityHolderProxy = Proxy.newProxyInstance(
+ sdkClassLoader,
+ arrayOf(activityHolderClass),
+ handler
+ )
+
+ val lifecycleProxy = lifecycleRegistryProxyFactory.setupLifecycleProxy(
+ activityHolderProxy,
+ activityHolder.lifecycle
+ )
+ handler.lifecycleProxy = lifecycleProxy
+
+ return activityHolderProxy
+ }
+
+ private class ActivityHolderHandler(
+ private val activity: Activity,
+ private val onBackPressedDispatcherProxy: Any,
+ ) : InvocationHandler {
+
+ var lifecycleProxy: Any? = null
+
+ override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any {
+ return when (method.name) {
+ "equals" -> proxy === args?.get(0)
+ "hashCode" -> hashCode()
+ "toString" -> toString()
+ "getActivity" -> activity
+ "getOnBackPressedDispatcher" -> onBackPressedDispatcherProxy
+ "getLifecycle" -> lifecycleProxy!!
+ else -> {
+ throw UnsupportedOperationException(
+ "Unexpected method call object:$proxy, method: $method, args: $args"
+ )
+ }
+ }
+ }
+ }
+
+ companion object {
+ fun createFor(classLoader: ClassLoader): ActivityHolderProxyFactory {
+ val activityHolderClass = Class.forName(
+ ActivityHolder::class.java.name,
+ /* initialize = */ false,
+ classLoader
+ )
+
+ val lifecycleRegistryProxyFactory = LifecycleRegistryProxyFactory.createFor(classLoader)
+ val backPressedDispatcherProxyFactory =
+ BackPressedDispatcherProxyFactory.createFor(classLoader)
+
+ return ActivityHolderProxyFactory(
+ sdkClassLoader = classLoader,
+ activityHolderClass = activityHolderClass,
+ backPressedDispatcherProxyFactory = backPressedDispatcherProxyFactory,
+ lifecycleRegistryProxyFactory = lifecycleRegistryProxyFactory
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/BackPressedDispatcherProxyFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/BackPressedDispatcherProxyFactory.kt
new file mode 100644
index 0000000..794d5c7
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/BackPressedDispatcherProxyFactory.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import android.annotation.SuppressLint
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.OnBackPressedDispatcher
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+
+/**
+ * Create instance of [OnBackPressedDispatcher] class loaded by SDK Classloader.
+ * Set callback for [OnBackPressedDispatcher.onHasEnabledCallbacksChanged] and enable/disable
+ * callback in original [OnBackPressedDispatcher].
+ * Proxy [OnBackPressedDispatcher.onBackPressed] from original dispatcher to proxy.
+ */
+internal class BackPressedDispatcherProxyFactory(
+ private val onBackPressedDispatcherConstructor: Constructor<out Any>,
+ private val consumerClass: Class<*>,
+ private val dispatcherOnBackPressedMethod: Method,
+ private val sdkClassLoader: ClassLoader
+) {
+ fun setupOnBackPressedDispatcherProxy(
+ sourceDispatcher: OnBackPressedDispatcher
+ ): Any {
+ val enabledChangedHandler = OnHasEnabledCallbacksChangedHandler()
+
+ val onHasEnabledCallbacksChangedCallback = Proxy.newProxyInstance(
+ sdkClassLoader,
+ arrayOf(consumerClass),
+ enabledChangedHandler
+ )
+ val dispatcherProxy = onBackPressedDispatcherConstructor.newInstance(
+ /* parameter1 */ null,
+ /* parameter2 */ onHasEnabledCallbacksChangedCallback
+ )
+
+ val sourceDispatcherCallback =
+ SourceDispatcherCallback(dispatcherProxy, dispatcherOnBackPressedMethod)
+
+ enabledChangedHandler.sourceDispatcherCallback = sourceDispatcherCallback
+
+ sourceDispatcher.addCallback(sourceDispatcherCallback)
+
+ return dispatcherProxy
+ }
+
+ private class OnHasEnabledCallbacksChangedHandler : InvocationHandler {
+
+ var sourceDispatcherCallback: OnBackPressedCallback? = null
+
+ override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any {
+ return when (method.name) {
+ "equals" -> proxy === args?.get(0)
+ "hashCode" -> hashCode()
+ "toString" -> toString()
+ "accept" -> sourceDispatcherCallback?.isEnabled = args!![0] as Boolean
+ else -> {
+ throw UnsupportedOperationException(
+ "Unexpected method call object:$proxy, method: $method, args: $args"
+ )
+ }
+ }
+ }
+ }
+
+ private class SourceDispatcherCallback(
+ private val dispatcherProxy: Any,
+ private val dispatcherOnBackPressedMethod: Method,
+ ) : OnBackPressedCallback(false) {
+
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun handleOnBackPressed() {
+ dispatcherOnBackPressedMethod.invoke(dispatcherProxy)
+ }
+ }
+
+ companion object {
+ fun createFor(classLoader: ClassLoader): BackPressedDispatcherProxyFactory {
+ val onBackPressedDispatcherClass = Class.forName(
+ "androidx.activity.OnBackPressedDispatcher",
+ /* initialize = */ false,
+ classLoader
+ )
+
+ val consumerClass = Class.forName(
+ "androidx.core.util.Consumer",
+ /* initialize = */ false,
+ classLoader
+ )
+
+ val onBackPressedDispatcherConstructor = onBackPressedDispatcherClass.getConstructor(
+ /* parameter1 */ Runnable::class.java,
+ /* parameter2 */ consumerClass
+ )
+
+ val dispatcherOnBackPressedMethod = onBackPressedDispatcherClass
+ .getMethod("onBackPressed")
+
+ return BackPressedDispatcherProxyFactory(
+ onBackPressedDispatcherConstructor,
+ consumerClass,
+ dispatcherOnBackPressedMethod,
+ classLoader
+ )
+ }
+ }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/LifecycleRegistryProxyFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/LifecycleRegistryProxyFactory.kt
new file mode 100644
index 0000000..509cf50
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/LifecycleRegistryProxyFactory.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import android.annotation.SuppressLint
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import java.lang.reflect.Constructor
+import java.lang.reflect.Method
+
+/**
+ * Create proxy of [Lifecycle] that implements same interface loaded by SDK Classloader.
+ * Proxy [Lifecycle.Event] from original object to proxy.
+ */
+internal class LifecycleRegistryProxyFactory private constructor(
+ private val lifecycleRegistryConstructor: Constructor<out Any>,
+ private val lifecycleEventInstances: Map<String, Any>,
+ private val handleLifecycleEventMethod: Method
+) {
+ fun setupLifecycleProxy(
+ activityHolderProxy: Any,
+ sourceLifecycle: Lifecycle
+ ): Any {
+ val registry = lifecycleRegistryConstructor.newInstance(activityHolderProxy)
+ sourceLifecycle.addObserver(object : LifecycleEventObserver {
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ val enumInstance = lifecycleEventInstances[event.name]
+ if (enumInstance != null) {
+ handleLifecycleEventMethod.invoke(registry, enumInstance)
+ }
+ }
+ })
+ return registry
+ }
+
+ companion object {
+ fun createFor(classLoader: ClassLoader): LifecycleRegistryProxyFactory {
+ val lifecycleOwnerClass = Class.forName(
+ "androidx.lifecycle.LifecycleOwner",
+ /* initialize = */ false,
+ classLoader
+ )
+ val lifecycleRegistryClass = Class.forName(
+ "androidx.lifecycle.LifecycleRegistry",
+ /* initialize = */ false,
+ classLoader
+ )
+ val lifecycleRegistryConstructor =
+ lifecycleRegistryClass.getConstructor(
+ /* parameter1 */ lifecycleOwnerClass
+ )
+
+ val lifecycleEventEnum =
+ Class.forName(
+ Lifecycle.Event::class.java.name,
+ /* initialize = */ false,
+ classLoader
+ )
+ val lifecycleEventInstances = lifecycleEventEnum
+ .enumConstants
+ .filterIsInstance(Enum::class.java)
+ .associateBy({ it.name }, { it })
+
+ val handleLifecycleEventMethod = lifecycleRegistryClass.getMethod(
+ "handleLifecycleEvent",
+ /* parameter1 */ lifecycleEventEnum
+ )
+
+ return LifecycleRegistryProxyFactory(
+ lifecycleRegistryConstructor,
+ lifecycleEventInstances,
+ handleLifecycleEventMethod
+ )
+ }
+ }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapper.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapper.kt
new file mode 100644
index 0000000..d9549f9
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/injector/SdkActivityHandlerWrapper.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.impl.injector
+
+import android.annotation.SuppressLint
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import java.lang.reflect.Method
+
+/**
+ * Creates reflection wrapper for implementation of [SdkSandboxActivityHandlerCompat] interface
+ * loaded by SDK classloader.
+ */
+internal class SdkActivityHandlerWrapper private constructor(
+ private val activityHolderProxyFactory: ActivityHolderProxyFactory,
+ private val handlerOnActivityCreatedMethod: Method,
+) {
+
+ fun wrapSdkSandboxActivityHandlerCompat(handlerCompat: Any): SdkSandboxActivityHandlerCompat =
+ WrappedHandler(handlerCompat, handlerOnActivityCreatedMethod, activityHolderProxyFactory)
+
+ private class WrappedHandler(
+ private val originalHandler: Any,
+ private val handlerOnActivityCreatedMethod: Method,
+ private val activityHolderProxyFactory: ActivityHolderProxyFactory
+ ) : SdkSandboxActivityHandlerCompat {
+
+ @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+ override fun onActivityCreated(activityHolder: ActivityHolder) {
+ val activityHolderProxy = activityHolderProxyFactory.createProxyFor(activityHolder)
+ handlerOnActivityCreatedMethod.invoke(originalHandler, activityHolderProxy)
+ }
+ }
+
+ companion object {
+ fun createFor(classLoader: ClassLoader): SdkActivityHandlerWrapper {
+ val sdkSandboxActivityHandlerCompatClass = Class.forName(
+ SdkSandboxActivityHandlerCompat::class.java.name,
+ /* initialize = */ false,
+ classLoader
+ )
+ val activityHolderClass = Class.forName(
+ ActivityHolder::class.java.name,
+ /* initialize = */ false,
+ classLoader
+ )
+ val handlerOnActivityCreatedMethod =
+ sdkSandboxActivityHandlerCompatClass.getMethod(
+ "onActivityCreated",
+ activityHolderClass
+ )
+
+ val activityHolderProxyFactory = ActivityHolderProxyFactory.createFor(classLoader)
+
+ return SdkActivityHandlerWrapper(
+ activityHolderProxyFactory = activityHolderProxyFactory,
+ handlerOnActivityCreatedMethod = handlerOnActivityCreatedMethod
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt b/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
index 56a2116..e062bff 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
@@ -1,6 +1,13 @@
// Signature format: 4.0
package androidx.privacysandbox.sdkruntime.core {
+ public final class AppOwnedSdkSandboxInterfaceCompat {
+ ctor public AppOwnedSdkSandboxInterfaceCompat(String name, long version, android.os.IBinder binder);
+ method public android.os.IBinder getInterface();
+ method public String getName();
+ method public long getVersion();
+ }
+
public final class LoadSdkCompatException extends java.lang.Exception {
ctor public LoadSdkCompatException(Throwable cause, android.os.Bundle extraInfo);
method public android.os.Bundle getExtraInformation();
@@ -51,11 +58,27 @@
}
+package androidx.privacysandbox.sdkruntime.core.activity {
+
+ public interface ActivityHolder extends androidx.lifecycle.LifecycleOwner {
+ method public android.app.Activity getActivity();
+ method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+ }
+
+ public interface SdkSandboxActivityHandlerCompat {
+ method public void onActivityCreated(androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder activityHolder);
+ }
+
+}
+
package androidx.privacysandbox.sdkruntime.core.controller {
public final class SdkSandboxControllerCompat {
method public static androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+ method public java.util.List<androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat> getAppOwnedSdkSandboxInterfaces();
method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+ method public android.os.IBinder registerSdkSandboxActivityHandler(androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat handlerCompat);
+ method public void unregisterSdkSandboxActivityHandler(androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat handlerCompat);
field public static final androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat.Companion Companion;
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt b/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
index 56a2116..e062bff 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
@@ -1,6 +1,13 @@
// Signature format: 4.0
package androidx.privacysandbox.sdkruntime.core {
+ public final class AppOwnedSdkSandboxInterfaceCompat {
+ ctor public AppOwnedSdkSandboxInterfaceCompat(String name, long version, android.os.IBinder binder);
+ method public android.os.IBinder getInterface();
+ method public String getName();
+ method public long getVersion();
+ }
+
public final class LoadSdkCompatException extends java.lang.Exception {
ctor public LoadSdkCompatException(Throwable cause, android.os.Bundle extraInfo);
method public android.os.Bundle getExtraInformation();
@@ -51,11 +58,27 @@
}
+package androidx.privacysandbox.sdkruntime.core.activity {
+
+ public interface ActivityHolder extends androidx.lifecycle.LifecycleOwner {
+ method public android.app.Activity getActivity();
+ method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+ }
+
+ public interface SdkSandboxActivityHandlerCompat {
+ method public void onActivityCreated(androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder activityHolder);
+ }
+
+}
+
package androidx.privacysandbox.sdkruntime.core.controller {
public final class SdkSandboxControllerCompat {
method public static androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+ method public java.util.List<androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat> getAppOwnedSdkSandboxInterfaces();
method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+ method public android.os.IBinder registerSdkSandboxActivityHandler(androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat handlerCompat);
+ method public void unregisterSdkSandboxActivityHandler(androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat handlerCompat);
field public static final androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat.Companion Companion;
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
index 68c7464..ca4567e 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
@@ -26,7 +26,8 @@
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.6.0")
- implementation("androidx.core:core:1.8.0")
+ implementation("androidx.core:core:1.12.0-alpha03")
+ implementation(projectOrArtifact(":activity:activity"))
// TODO(b/249982004): cleanup dependencies
androidTestImplementation(libs.testCore)
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/lint-baseline.xml b/privacysandbox/sdkruntime/sdkruntime-core/lint-baseline.xml
new file mode 100644
index 0000000..4f913f5e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta01)" variant="all" version="8.1.0-beta01">
+
+ <issue
+ id="PrereleaseSdkCoreDependency"
+ message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
+ errorLine1=" if (BuildCompat.isAtLeastU()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt"/>
+ </issue>
+
+</issues>
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompatTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompatTest.kt
new file mode 100644
index 0000000..22d69b5
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompatTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core
+
+import android.os.Binder
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 35, codeName = "UpsideDownCakePrivacySandbox")
+class AppOwnedSdkSandboxInterfaceCompatTest {
+
+ @Test
+ fun converterTest() {
+ val converter = AppOwnedInterfaceConverter()
+
+ val compatObj = AppOwnedSdkSandboxInterfaceCompat(
+ name = "SDK",
+ version = 1,
+ binder = Binder()
+ )
+
+ val platformObj = converter.toPlatform(compatObj)
+ assertThat(platformObj.javaClass.name)
+ .isEqualTo("android.app.sdksandbox.AppOwnedSdkSandboxInterface")
+
+ val convertedCompatObj = converter.toCompat(platformObj)
+
+ assertThat(convertedCompatObj.getName()).isEqualTo(compatObj.getName())
+ assertThat(convertedCompatObj.getVersion()).isEqualTo(compatObj.getVersion())
+ assertThat(convertedCompatObj.getInterface()).isEqualTo(compatObj.getInterface())
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerAppOwnedInterfacesTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerAppOwnedInterfacesTest.kt
new file mode 100644
index 0000000..a9c750d
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerAppOwnedInterfacesTest.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.controller
+
+import android.annotation.SuppressLint
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
+import android.content.Context
+import android.os.Binder
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedInterfaceConverter
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+
+// TODO(b/249982507) Rewrite test to use real SDK in sandbox instead of mocking controller
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+class SdkSandboxControllerAppOwnedInterfacesTest {
+
+ @Test
+ fun getAppOwnedSdkSandboxInterfaces_whenControllerNotAvailable_returnsEmptyList() {
+ assumeFalse(
+ "Requires SandboxController not available",
+ isSandboxControllerAvailable()
+ )
+
+ val context = ApplicationProvider.getApplicationContext<Context>()
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+ val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(appOwnedInterfaces).isEmpty()
+ }
+
+ @Test
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ fun getAppOwnedSdkSandboxInterfaces_whenApiNotAvailable_returnsEmptyList() {
+ assumeTrue(
+ "Requires SandboxController available",
+ isSandboxControllerAvailable()
+ )
+ assumeFalse(
+ "Requires AppOwnedInterfaces API not available",
+ isAppOwnedInterfacesApiAvailable()
+ )
+
+ val context = spy(ApplicationProvider.getApplicationContext<Context>())
+ val sdkSandboxController = mock(SdkSandboxController::class.java)
+ doReturn(sdkSandboxController)
+ .`when`(context).getSystemService(SdkSandboxController::class.java)
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+ val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(appOwnedInterfaces).isEmpty()
+ verifyZeroInteractions(sdkSandboxController)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 35, codeName = "UpsideDownCakePrivacySandbox")
+ fun getAppOwnedSdkSandboxInterfaces_whenApiAvailable_delegateToPlatform() {
+ val context = spy(ApplicationProvider.getApplicationContext<Context>())
+ val controllerMock = mock(SdkSandboxController::class.java)
+ doReturn(controllerMock)
+ .`when`(context).getSystemService(SdkSandboxController::class.java)
+
+ val expectedObj = AppOwnedSdkSandboxInterfaceCompat(
+ name = "test",
+ version = 1,
+ binder = Binder()
+ )
+ setupMockedMethodResult(controllerMock, expectedObj)
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+ val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(appOwnedInterfaces).hasSize(1)
+ val resultObj = appOwnedInterfaces[0]
+
+ assertThat(resultObj.getName()).isEqualTo(expectedObj.getName())
+ assertThat(resultObj.getVersion()).isEqualTo(expectedObj.getVersion())
+ assertThat(resultObj.getInterface()).isEqualTo(expectedObj.getInterface())
+ }
+
+ private fun isSandboxControllerAvailable() =
+ AdServicesInfo.isAtLeastV5()
+
+ private fun isAppOwnedInterfacesApiAvailable() =
+ AdServicesInfo.isDeveloperPreview()
+
+ companion object AppOwnedInterfacesApi { // to avoid fail if SdkSandboxController not present
+ @SuppressLint("NewApi", "ClassVerificationFailure") // UpsideDownCakePrivacySandbox is UDC
+ private fun setupMockedMethodResult(
+ controllerMock: SdkSandboxController,
+ result: AppOwnedSdkSandboxInterfaceCompat
+ ) {
+ val getAppOwnedInterfacesMethod = SdkSandboxController::class.java.getDeclaredMethod(
+ "getAppOwnedSdkSandboxInterfaces"
+ )
+
+ val platformObj = AppOwnedInterfaceConverter().toPlatform(result)
+
+ // Mockito require method call to setup result. Reflection call works same way as normal.
+ `when`(getAppOwnedInterfacesMethod.invoke(controllerMock))
+ .thenReturn(listOf(platformObj))
+ }
+ }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
index 1307cec..d2eb922 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
@@ -18,13 +18,18 @@
import android.content.Context
import android.os.Binder
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.After
+import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,9 +76,107 @@
assertThat(sandboxedSdks).isEqualTo(expectedResult)
}
- private class TestStubImpl(
+ @Test
+ fun getAppOwnedSdkSandboxInterfaces_withoutLocalImpl_returnsEmptyList() {
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(appOwnedInterfaces).isEmpty()
+ }
+
+ @Test
+ fun getAppOwnedSdkSandboxInterfaces_withLocalImpl_returnsEmptyList() {
+ SdkSandboxControllerCompat.injectLocalImpl(
+ TestStubImpl()
+ )
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
+ assertThat(appOwnedInterfaces).isEmpty()
+ }
+
+ @Test
+ fun registerSdkSandboxActivityHandler_withLocalImpl_registerItInLocalImpl() {
+ val localImpl = TestStubImpl()
+ SdkSandboxControllerCompat.injectLocalImpl(localImpl)
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ val handlerCompat = object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {}
+ }
+ val token = controllerCompat.registerSdkSandboxActivityHandler(handlerCompat)
+ assertThat(token).isEqualTo(localImpl.token)
+ }
+
+ @Test
+ fun unregisterSdkSandboxActivityHandler_withLocalImpl_unregisterItFromLocalImpl() {
+ val localImpl = TestStubImpl()
+ SdkSandboxControllerCompat.injectLocalImpl(localImpl)
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ val handlerCompat = object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {}
+ }
+ val token = controllerCompat.registerSdkSandboxActivityHandler(handlerCompat)
+ assertThat(token).isEqualTo(localImpl.token)
+
+ controllerCompat.unregisterSdkSandboxActivityHandler(handlerCompat)
+ assertThat(localImpl.token).isNull()
+ }
+
+ @Test
+ fun registerSdkSandboxActivityHandler_clientApiBelow3_throwsUnsupportedOperationException() {
+ // Emulate loading via client lib with version below 3
+ Versions.handShake(2)
+
+ SdkSandboxControllerCompat.injectLocalImpl(TestStubImpl())
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ controllerCompat.registerSdkSandboxActivityHandler(
+ object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {}
+ }
+ )
+ }
+ }
+
+ @Test
+ fun unregisterSdkSandboxActivityHandler_clientApiBelow3_throwsUnsupportedOperationException() {
+ // Emulate loading via client lib with version below 3
+ Versions.handShake(2)
+
+ SdkSandboxControllerCompat.injectLocalImpl(TestStubImpl())
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ controllerCompat.unregisterSdkSandboxActivityHandler(
+ object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {}
+ }
+ )
+ }
+ }
+
+ internal class TestStubImpl(
private val sandboxedSdks: List<SandboxedSdkCompat> = emptyList()
) : SdkSandboxControllerCompat.SandboxControllerImpl {
+ var token: IBinder? = null
override fun getSandboxedSdks() = sandboxedSdks
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ throw IllegalStateException("Must return result without calling method")
+ }
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ token = Binder()
+ return token!!
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ token = null
+ }
}
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt
index b133fe0..bcac996 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt
@@ -16,23 +16,35 @@
package androidx.privacysandbox.sdkruntime.core.controller
+import android.app.Activity
+import android.app.Application
import android.app.sdksandbox.SandboxedSdk
+import android.app.sdksandbox.sdkprovider.SdkSandboxActivityHandler
import android.app.sdksandbox.sdkprovider.SdkSandboxController
import android.content.Context
import android.os.Binder
import android.os.Build
+import android.os.Bundle
import android.os.ext.SdkExtensions
+import android.window.OnBackInvokedDispatcher
import androidx.annotation.RequiresExtension
+import androidx.lifecycle.Lifecycle
import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SdkSuppress
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Test
+import org.mockito.ArgumentCaptor
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when`
@@ -41,7 +53,7 @@
class SdkSandboxControllerCompatSandboxedTest {
@Test
- fun getSandboxedSdks_whenApiNotAvailable_notDelegateToSandbox() {
+ fun controllerAPIs_whenApiNotAvailable_notDelegateToSandbox() {
assumeFalse(
"Requires SandboxController API not available",
isSandboxControllerAvailable()
@@ -51,7 +63,15 @@
val controllerCompat = SdkSandboxControllerCompat.from(context)
controllerCompat.getSandboxedSdks()
-
+ val handlerCompat = object : SdkSandboxActivityHandlerCompat {
+ override fun onActivityCreated(activityHolder: ActivityHolder) {}
+ }
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ controllerCompat.registerSdkSandboxActivityHandler(handlerCompat)
+ }
+ Assert.assertThrows(UnsupportedOperationException::class.java) {
+ controllerCompat.unregisterSdkSandboxActivityHandler(handlerCompat)
+ }
verifyZeroInteractions(context)
}
@@ -97,6 +117,120 @@
assertThat(result.getInterface()).isEqualTo(sandboxedSdk.getInterface())
}
+ @Test
+ // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ fun registerSdkSandboxHandlerCompat_whenApiAvailable_registerItToPlatform() {
+ val context = spy(ApplicationProvider.getApplicationContext<Context>())
+ val sdkSandboxController = mock(SdkSandboxController::class.java)
+ doReturn(sdkSandboxController)
+ .`when`(context).getSystemService(SdkSandboxController::class.java)
+
+ val platformRegisteredHandlerCaptor = ArgumentCaptor.forClass(
+ SdkSandboxActivityHandler::class.java)
+ doReturn(Binder())
+ .`when`(sdkSandboxController).registerSdkSandboxActivityHandler(
+ platformRegisteredHandlerCaptor.capture())
+
+ val handlerCompat = mock(SdkSandboxActivityHandlerCompat::class.java)
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ controllerCompat.registerSdkSandboxActivityHandler(handlerCompat)
+
+ verify(sdkSandboxController).registerSdkSandboxActivityHandler(
+ platformRegisteredHandlerCaptor.value
+ )
+
+ val activityMock = mock(Activity::class.java)
+ val onBackInvokedDispatcher = mock(OnBackInvokedDispatcher::class.java)
+ doReturn(onBackInvokedDispatcher).`when`(activityMock).onBackInvokedDispatcher
+
+ platformRegisteredHandlerCaptor.value.onActivityCreated(activityMock)
+ var activityHolderCaptor: ArgumentCaptor<ActivityHolder> =
+ ArgumentCaptor.forClass(ActivityHolder::class.java)
+ verify(handlerCompat).onActivityCreated(capture(activityHolderCaptor))
+ assertThat(activityHolderCaptor.value.getActivity()).isEqualTo(activityMock)
+
+ assertThat(activityHolderCaptor.value.getOnBackPressedDispatcher()).isNotNull()
+
+ assertThat(activityHolderCaptor.value.lifecycle).isNotNull()
+ var activityLifecycleCallbackCaptor:
+ ArgumentCaptor<Application.ActivityLifecycleCallbacks> =
+ ArgumentCaptor.forClass(Application.ActivityLifecycleCallbacks::class.java)
+ verify(activityMock).registerActivityLifecycleCallbacks(
+ activityLifecycleCallbackCaptor.capture()
+ )
+ var bundleMock = mock(Bundle::class.java)
+ UiThreadStatement.runOnUiThread {
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.INITIALIZED)
+ activityLifecycleCallbackCaptor.value.onActivityCreated(activityMock, bundleMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.CREATED)
+
+ activityLifecycleCallbackCaptor.value.onActivityStarted(activityMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.STARTED)
+
+ activityLifecycleCallbackCaptor.value.onActivityResumed(activityMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.RESUMED)
+
+ activityLifecycleCallbackCaptor.value.onActivityPaused(activityMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.STARTED)
+
+ activityLifecycleCallbackCaptor.value.onActivityStopped(activityMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.CREATED)
+
+ activityLifecycleCallbackCaptor.value.onActivityDestroyed(activityMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(
+ Lifecycle.State.DESTROYED)
+
+ val currentState = activityHolderCaptor.value.lifecycle.currentState
+ activityLifecycleCallbackCaptor.value.onActivitySaveInstanceState(
+ activityMock, bundleMock)
+ assertThat(activityHolderCaptor.value.lifecycle.currentState).isEqualTo(currentState)
+ }
+ }
+
+ @Test
+ // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ fun unregisterSdkSandboxHandlerCompat_whenApiAvailable_unregisterItToPlatform() {
+ val context = spy(ApplicationProvider.getApplicationContext<Context>())
+ val sdkSandboxController = mock(SdkSandboxController::class.java)
+ doReturn(sdkSandboxController)
+ .`when`(context).getSystemService(SdkSandboxController::class.java)
+
+ val registeredHandlerCaptor = ArgumentCaptor.forClass(SdkSandboxActivityHandler::class.java)
+ doReturn(Binder())
+ .`when`(sdkSandboxController).registerSdkSandboxActivityHandler(
+ registeredHandlerCaptor.capture())
+
+ val controllerCompat = SdkSandboxControllerCompat.from(context)
+ val handlerCompat = mock(SdkSandboxActivityHandlerCompat::class.java)
+
+ controllerCompat.registerSdkSandboxActivityHandler(handlerCompat)
+ verify(sdkSandboxController).registerSdkSandboxActivityHandler(
+ registeredHandlerCaptor.value)
+
+ val unregisteredHandlerCaptor = ArgumentCaptor.forClass(
+ SdkSandboxActivityHandler::class.java
+ )
+ controllerCompat.unregisterSdkSandboxActivityHandler(handlerCompat)
+ verify(sdkSandboxController).unregisterSdkSandboxActivityHandler(
+ unregisteredHandlerCaptor.capture())
+
+ assertThat(unregisteredHandlerCaptor.value).isEqualTo(registeredHandlerCaptor.value)
+ }
+
private fun isSandboxControllerAvailable() =
AdServicesInfo.isAtLeastV5()
+
+ // To capture non null arguments.
+
+ private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt
index 67e884b..3cc4ee9 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt
@@ -18,6 +18,7 @@
import android.os.Build
import android.os.ext.SdkExtensions
+import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
@@ -41,6 +42,10 @@
Extensions30Impl.isAtLeastV5()
}
+ @ChecksSdkIntAtLeast(codename = "UpsideDownCake")
+ fun isDeveloperPreview(): Boolean =
+ Build.VERSION.CODENAME.equals("UpsideDownCakePrivacySandbox")
+
@RequiresApi(30)
private object Extensions30Impl {
@DoNotInline
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedInterfaceConverter.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedInterfaceConverter.kt
new file mode 100644
index 0000000..b28e5e3
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedInterfaceConverter.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core
+
+import android.annotation.SuppressLint
+import android.os.IBinder
+import androidx.annotation.RestrictTo
+
+/**
+ * Temporary converter of [AppOwnedSdkSandboxInterfaceCompat] to platform
+ * AppOwnedSdkSandboxInterface and back.
+ *
+ * Could be used only with Developer Preview 8 build.
+ * Using reflection to call public methods / constructors.
+ *
+ * TODO(b/281397807) Should be removed as soon as prebuilt with new API will be available.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class AppOwnedInterfaceConverter {
+
+ private val appOwnedInterfaceClass = Class.forName(
+ "android.app.sdksandbox.AppOwnedSdkSandboxInterface"
+ )
+
+ private val appOwnedInterfaceConstructor = appOwnedInterfaceClass.getConstructor(
+ /* name */ String::class.java,
+ /* version */ Long::class.java,
+ /* interface */ IBinder::class.java
+ )
+
+ private val getNameMethod = appOwnedInterfaceClass.getMethod("getName")
+
+ private val getVersionMethod = appOwnedInterfaceClass.getMethod("getVersion")
+
+ private val getInterfaceMethod = appOwnedInterfaceClass.getMethod("getInterface")
+
+ @SuppressLint("BanUncheckedReflection") // calling public non restricted methods
+ fun toCompat(platformObject: Any): AppOwnedSdkSandboxInterfaceCompat {
+ val name = getNameMethod.invoke(platformObject) as String
+ val version = getVersionMethod.invoke(platformObject) as Long
+ val binder = getInterfaceMethod.invoke(platformObject) as IBinder
+ return AppOwnedSdkSandboxInterfaceCompat(name, version, binder)
+ }
+
+ fun toPlatform(compatObject: AppOwnedSdkSandboxInterfaceCompat): Any {
+ return appOwnedInterfaceConstructor.newInstance(
+ compatObject.getName(),
+ compatObject.getVersion(),
+ compatObject.getInterface()
+ )
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompat.kt
new file mode 100644
index 0000000..8a4b4cd
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AppOwnedSdkSandboxInterfaceCompat.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core
+
+import android.os.IBinder
+
+/**
+ * Represents a channel for an SDK in the sandbox process to interact with the app.
+ *
+ * The SDK and the app can agree on a binder interface to be implemented by the app and shared
+ * via an object of [AppOwnedSdkSandboxInterfaceCompat].
+ *
+ * The app registers the AppOwnedSdkSandboxInterfaces using SdkSandboxManagerCompat.
+ * The SDK can then query the list of registered interfaces using SdkSandboxControllerCompat.
+ *
+ * Once SDK has the [AppOwnedSdkSandboxInterfaceCompat] it wants to communicate with, it will have
+ * to cast the binder object from [getInterface] to the prearranged interface before initiating
+ * the communication.
+ */
+class AppOwnedSdkSandboxInterfaceCompat(
+ private val name: String,
+ private val version: Long,
+ private val binder: IBinder
+) {
+
+ /**
+ * Returns the name used to register the [AppOwnedSdkSandboxInterfaceCompat].
+ *
+ * App can register only one interface of given name.
+ */
+ fun getName(): String = name
+
+ /**
+ * Returns the version used to register the [AppOwnedSdkSandboxInterfaceCompat].
+ *
+ * A version may be chosen by an app, and used to communicate any updates the app makes to
+ * this implementation.
+ */
+ fun getVersion(): Long = version
+
+ /**
+ * Returns binder object associated with [AppOwnedSdkSandboxInterfaceCompat].
+ *
+ * The SDK and the app can agree on a binder interface to be implemented by the app and
+ * shared via this object.
+ *
+ * The SDK in the sandbox will have to cast the binder object received from this method to
+ * the agreed upon interface before using it.
+ */
+ fun getInterface(): IBinder = binder
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt
index 18585d0..9ba440d 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt
@@ -31,7 +31,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object Versions {
- const val API_VERSION = 2
+ const val API_VERSION = 3
@JvmField
var CLIENT_VERSION: Int? = null
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/ActivityHolder.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/ActivityHolder.kt
new file mode 100644
index 0000000..8832f0e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/ActivityHolder.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.activity
+
+import android.app.Activity
+import androidx.activity.OnBackPressedDispatcher
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+
+/**
+ * A holder for the [Activity] created for SDK.
+ *
+ * This is passed to SDKs through [SdkSandboxActivityHandlerCompat.onActivityCreated] to notify SDKs
+ * about the created [Activity].
+ *
+ * SDK can add [LifecycleObserver]s into it to observe the [Activity] lifecycle state.
+ */
+interface ActivityHolder : LifecycleOwner {
+ /**
+ * The [Activity] created for SDK.
+ */
+ fun getActivity(): Activity
+
+ /**
+ * The [OnBackPressedDispatcher] for the created [Activity].
+ */
+ fun getOnBackPressedDispatcher(): OnBackPressedDispatcher
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/SdkSandboxActivityHandlerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/SdkSandboxActivityHandlerCompat.kt
new file mode 100644
index 0000000..b41250a
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/activity/SdkSandboxActivityHandlerCompat.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.activity
+
+import android.app.Activity
+import android.app.sdksandbox.sdkprovider.SdkSandboxActivityHandler
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * This is used to notify the SDK when an [Activity] is created for it.
+ *
+ * When an SDK wants to start an [Activity], it should register an implementation of this class by
+ * calling [SdkSandboxControllerCompat.registerSdkSandboxActivityHandler] that will return an
+ * [android.os.Binder] identifier for the registered [SdkSandboxControllerCompat].
+ *
+ * The SDK should be notified about the [Activity] creation through calling
+ * [SdkSandboxActivityHandlerCompat.onActivityCreated] which happens when the caller app calls
+ * `SdkSandboxManagerCompat#startSdkSandboxActivity(Activity, IBinder)` using the same
+ * [android.os.IBinder] identifier for the registered [SdkSandboxActivityHandlerCompat].
+ *
+ * @see SdkSandboxActivityHandler
+ */
+interface SdkSandboxActivityHandlerCompat {
+
+ /**
+ * Notifies SDK when an [Activity] gets created.
+ *
+ * This function is called synchronously from the main thread of the [Activity] that is getting
+ * created.
+ *
+ * SDK is expected to call [Activity.setContentView] to the passed [Activity] object to populate
+ * the view.
+ *
+ * @param activityHolder the [ActivityHolder] which holds the [Activity] which gets created
+ * @see SdkSandboxActivityHandler.onActivityCreated
+ */
+ fun onActivityCreated(activityHolder: ActivityHolder)
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt
index 9575052..31494d2 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt
@@ -16,16 +16,24 @@
package androidx.privacysandbox.sdkruntime.core.controller
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
import android.content.Context
+import android.os.IBinder
import androidx.annotation.Keep
+import androidx.annotation.OptIn
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.core.os.BuildCompat
import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import androidx.privacysandbox.sdkruntime.core.controller.impl.LocalImpl
import androidx.privacysandbox.sdkruntime.core.controller.impl.NoOpImpl
import androidx.privacysandbox.sdkruntime.core.controller.impl.PlatformImpl
+import androidx.privacysandbox.sdkruntime.core.controller.impl.PlatformUDCImpl
import org.jetbrains.annotations.TestOnly
/**
@@ -56,10 +64,56 @@
fun getSandboxedSdks(): List<SandboxedSdkCompat> =
controllerImpl.getSandboxedSdks()
+ /**
+ * Fetches all [AppOwnedSdkSandboxInterfaceCompat] that are registered by the app.
+ *
+ * @return List of all currently registered [AppOwnedSdkSandboxInterfaceCompat]
+ */
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ controllerImpl.getAppOwnedSdkSandboxInterfaces()
+
+ /**
+ * Returns an identifier for a [SdkSandboxActivityHandlerCompat] after registering it.
+ *
+ * This function registers an implementation of [SdkSandboxActivityHandlerCompat] created by
+ * an SDK and returns an [IBinder] which uniquely identifies the passed
+ * [SdkSandboxActivityHandlerCompat] object.
+ *
+ * @param handlerCompat is the [SdkSandboxActivityHandlerCompat] to register
+ * @return [IBinder] uniquely identify the passed [SdkSandboxActivityHandlerCompat]
+ * @see SdkSandboxController.registerSdkSandboxActivityHandler
+ */
+ fun registerSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat):
+ IBinder = controllerImpl.registerSdkSandboxActivityHandler(handlerCompat)
+
+ /**
+ * Unregister an already registered [SdkSandboxActivityHandlerCompat].
+ *
+ * If the passed [SdkSandboxActivityHandlerCompat] is registered, it will be unregistered.
+ * Otherwise, it will do nothing.
+ *
+ * If the [IBinder] token of the unregistered handler used to start a [android.app.Activity],
+ * the [android.app.Activity] will fail to start.
+ *
+ * @param handlerCompat is the [SdkSandboxActivityHandlerCompat] to unregister.
+ * @see SdkSandboxController.unregisterSdkSandboxActivityHandler
+ */
+ fun unregisterSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat) =
+ controllerImpl.unregisterSdkSandboxActivityHandler(handlerCompat)
+
/** @suppress */
@RestrictTo(LIBRARY_GROUP)
interface SandboxControllerImpl {
fun getSandboxedSdks(): List<SandboxedSdkCompat>
+
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat>
+
+ fun registerSdkSandboxActivityHandler(handlerCompat: SdkSandboxActivityHandlerCompat):
+ IBinder
+
+ fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ )
}
companion object {
@@ -75,20 +129,16 @@
*/
@JvmStatic
fun from(context: Context): SdkSandboxControllerCompat {
- val loadedLocally = Versions.CLIENT_VERSION != null
- if (loadedLocally) {
+ val clientVersion = Versions.CLIENT_VERSION
+ if (clientVersion != null) {
val implFromClient = localImpl
if (implFromClient != null) {
- return SdkSandboxControllerCompat(implFromClient)
+ return SdkSandboxControllerCompat(LocalImpl(implFromClient, clientVersion))
}
return SdkSandboxControllerCompat(NoOpImpl())
}
-
- if (AdServicesInfo.isAtLeastV5()) {
- return SdkSandboxControllerCompat(PlatformImpl.from(context))
- }
-
- return SdkSandboxControllerCompat(NoOpImpl())
+ val platformImpl = PlatformImplFactory.create(context)
+ return SdkSandboxControllerCompat(platformImpl)
}
/**
@@ -112,4 +162,17 @@
localImpl = null
}
}
+
+ private object PlatformImplFactory {
+ @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ fun create(context: Context): SandboxControllerImpl {
+ if (AdServicesInfo.isAtLeastV5()) {
+ if (BuildCompat.isAtLeastU() || AdServicesInfo.isDeveloperPreview()) {
+ return PlatformUDCImpl.from(context)
+ }
+ return PlatformImpl.from(context)
+ }
+ return NoOpImpl()
+ }
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/AppOwnedSdkProvider.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/AppOwnedSdkProvider.kt
new file mode 100644
index 0000000..bef25e8
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/AppOwnedSdkProvider.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.controller.impl
+
+import android.annotation.SuppressLint
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.AppOwnedInterfaceConverter
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+
+/**
+ * Fetches all registered [AppOwnedSdkSandboxInterfaceCompat] from [SdkSandboxController].
+ */
+internal class AppOwnedSdkProvider private constructor(
+ private val providerImpl: ProviderImpl
+) {
+
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ providerImpl.getAppOwnedSdkSandboxInterfaces()
+
+ private interface ProviderImpl {
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat>
+ }
+
+ /**
+ * Implementation for cases when API not supported by [SdkSandboxController]
+ */
+ private class NoOpImpl : ProviderImpl {
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ return emptyList()
+ }
+ }
+
+ /**
+ * Implementation for Developer Preview builds.
+ * Using reflection to call public methods / constructors.
+ * TODO(b/281397807) Replace reflection for method calls when new prebuilt will be available.
+ */
+ private class DeveloperPreviewImpl(
+ private val controller: SdkSandboxController
+ ) : ProviderImpl {
+
+ private val getAppOwnedSdkSandboxInterfacesMethod = controller.javaClass.getMethod(
+ "getAppOwnedSdkSandboxInterfaces",
+ )
+
+ private val converter: AppOwnedInterfaceConverter = AppOwnedInterfaceConverter()
+
+ @SuppressLint("BanUncheckedReflection") // calling public non restricted methods
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> {
+ val apiResult = getAppOwnedSdkSandboxInterfacesMethod.invoke(controller) as List<*>
+ return apiResult.map { converter.toCompat(it!!) }
+ }
+ }
+
+ companion object {
+ fun create(controller: SdkSandboxController): AppOwnedSdkProvider {
+ return if (AdServicesInfo.isDeveloperPreview()) {
+ AppOwnedSdkProvider(DeveloperPreviewImpl(controller))
+ } else {
+ AppOwnedSdkProvider(NoOpImpl())
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/LocalImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/LocalImpl.kt
new file mode 100644
index 0000000..0ab6b8a
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/LocalImpl.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.controller.impl
+
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * Wrapper for client provided implementation of [SdkSandboxControllerCompat].
+ * Checks client version to determine if method supported.
+ */
+internal class LocalImpl(
+ private val implFromClient: SdkSandboxControllerCompat.SandboxControllerImpl,
+ private val clientVersion: Int
+) : SdkSandboxControllerCompat.SandboxControllerImpl {
+ override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+ return implFromClient.getSandboxedSdks()
+ }
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ emptyList()
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ if (clientVersion < 3) {
+ throw UnsupportedOperationException(
+ "Client library version doesn't support SdkActivities"
+ )
+ }
+ return implFromClient.registerSdkSandboxActivityHandler(handlerCompat)
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ if (clientVersion < 3) {
+ throw UnsupportedOperationException(
+ "Client library version doesn't support SdkActivities"
+ )
+ }
+ implFromClient.unregisterSdkSandboxActivityHandler(handlerCompat)
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt
index cc0bfa3..cbe53ec 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt
@@ -16,7 +16,10 @@
package androidx.privacysandbox.sdkruntime.core.controller.impl
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
/**
@@ -24,4 +27,20 @@
*/
internal class NoOpImpl : SdkSandboxControllerCompat.SandboxControllerImpl {
override fun getSandboxedSdks(): List<SandboxedSdkCompat> = emptyList()
+
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ emptyList()
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ):
+ IBinder {
+ throw UnsupportedOperationException("Not supported")
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ throw UnsupportedOperationException("Not supported")
+ }
}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt
index ce2e035..7b487ec 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt
@@ -18,10 +18,13 @@
import android.app.sdksandbox.sdkprovider.SdkSandboxController
import android.content.Context
+import android.os.IBinder
import android.os.ext.SdkExtensions.AD_SERVICES
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
/**
@@ -29,16 +32,33 @@
*/
@RequiresApi(33)
@RequiresExtension(extension = AD_SERVICES, version = 5)
-internal class PlatformImpl(
+internal open class PlatformImpl(
private val controller: SdkSandboxController
) : SdkSandboxControllerCompat.SandboxControllerImpl {
+ private val appOwnedSdkProvider = AppOwnedSdkProvider.create(controller)
+
override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
return controller
.sandboxedSdks
.map { platformSdk -> SandboxedSdkCompat(platformSdk) }
}
+ override fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ appOwnedSdkProvider.getAppOwnedSdkSandboxInterfaces()
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ throw UnsupportedOperationException("This API only available for devices run on Android U+")
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ throw UnsupportedOperationException("This API only available for devices run on Android U+")
+ }
+
companion object {
fun from(context: Context): PlatformImpl {
val sdkSandboxController = context.getSystemService(SdkSandboxController::class.java)
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformUDCImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformUDCImpl.kt
new file mode 100644
index 0000000..fcae673
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformUDCImpl.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.controller.impl
+
+import android.app.Activity
+import android.app.Application
+import android.app.sdksandbox.sdkprovider.SdkSandboxActivityHandler
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
+import android.content.Context
+import android.os.Bundle
+import android.os.IBinder
+import android.os.ext.SdkExtensions
+import androidx.activity.OnBackPressedDispatcher
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresExtension
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+
+/**
+ * Implementation that delegates to platform [SdkSandboxController] for Android U.
+ */
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+@RequiresApi(34)
+internal class PlatformUDCImpl(
+ private val controller: SdkSandboxController
+) : PlatformImpl(controller) {
+
+ private val compatToPlatformMap =
+ hashMapOf<SdkSandboxActivityHandlerCompat, SdkSandboxActivityHandler>()
+
+ override fun registerSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ): IBinder {
+ synchronized(compatToPlatformMap) {
+ val platformHandler: SdkSandboxActivityHandler =
+ compatToPlatformMap[handlerCompat]
+ ?: SdkSandboxActivityHandler { platformActivity: Activity ->
+ handlerCompat.onActivityCreated(ActivityHolderImpl(platformActivity))
+ }
+ val token = controller.registerSdkSandboxActivityHandler(platformHandler)
+ compatToPlatformMap[handlerCompat] = platformHandler
+ return token
+ }
+ }
+
+ override fun unregisterSdkSandboxActivityHandler(
+ handlerCompat: SdkSandboxActivityHandlerCompat
+ ) {
+ synchronized(compatToPlatformMap) {
+ val platformHandler: SdkSandboxActivityHandler =
+ compatToPlatformMap[handlerCompat] ?: return
+ controller.unregisterSdkSandboxActivityHandler(platformHandler)
+ compatToPlatformMap.remove(handlerCompat)
+ }
+ }
+
+ internal class ActivityHolderImpl(
+ private val platformActivity: Activity
+ ) : ActivityHolder {
+ private val dispatcher = OnBackPressedDispatcher {}
+ private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
+
+ init {
+ // TODO(b/276315438) Set android:enableOnBackInvokedCallback="true" when
+ // creating the manifest file
+ dispatcher.setOnBackInvokedDispatcher(platformActivity.onBackInvokedDispatcher)
+ proxyLifeCycleEvents()
+ }
+
+ override fun getActivity(): Activity {
+ return platformActivity
+ }
+
+ override fun getOnBackPressedDispatcher(): OnBackPressedDispatcher {
+ return dispatcher
+ }
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ private fun proxyLifeCycleEvents() {
+ val callback = object : Application.ActivityLifecycleCallbacks {
+ override fun onActivityCreated(p0: Activity, p1: Bundle?) {
+ lifecycleRegistry.currentState = Lifecycle.State.CREATED
+ }
+
+ override fun onActivityStarted(p0: Activity) {
+ lifecycleRegistry.currentState = Lifecycle.State.STARTED
+ }
+
+ override fun onActivityResumed(p0: Activity) {
+ lifecycleRegistry.currentState = Lifecycle.State.RESUMED
+ }
+
+ override fun onActivityPaused(p0: Activity) {
+ lifecycleRegistry.currentState = Lifecycle.State.STARTED
+ }
+
+ override fun onActivityStopped(p0: Activity) {
+ lifecycleRegistry.currentState = Lifecycle.State.CREATED
+ }
+
+ override fun onActivityDestroyed(p0: Activity) {
+ lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
+ }
+
+ override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {
+ // No need for proxying
+ }
+ }
+ platformActivity.registerActivityLifecycleCallbacks(callback)
+ }
+ }
+
+ companion object {
+ fun from(context: Context): PlatformImpl {
+ val sdkSandboxController = context.getSystemService(SdkSandboxController::class.java)
+ return PlatformUDCImpl(sdkSandboxController)
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-client/api/current.txt b/privacysandbox/ui/ui-client/api/current.txt
index ae23335..bc9a40d 100644
--- a/privacysandbox/ui/ui-client/api/current.txt
+++ b/privacysandbox/ui/ui-client/api/current.txt
@@ -1,11 +1,20 @@
// Signature format: 4.0
package androidx.privacysandbox.ui.client {
+ public interface LocalSdkActivityLauncher<T extends android.app.Activity & androidx.lifecycle.LifecycleOwner> extends androidx.privacysandbox.ui.core.SdkActivityLauncher {
+ method public void dispose();
+ }
+
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class SandboxedUiAdapterFactory {
method public androidx.privacysandbox.ui.core.SandboxedUiAdapter createFromCoreLibInfo(android.os.Bundle coreLibInfo);
field public static final androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory INSTANCE;
}
+ public final class SdkActivityLaunchers {
+ method public static <T extends android.app.Activity & androidx.lifecycle.LifecycleOwner> androidx.privacysandbox.ui.client.LocalSdkActivityLauncher<T> createSdkActivityLauncher(T, kotlin.jvm.functions.Function0<java.lang.Boolean> allowLaunch);
+ method public static android.os.Bundle toLauncherInfo(androidx.privacysandbox.ui.core.SdkActivityLauncher);
+ }
+
}
package androidx.privacysandbox.ui.client.view {
diff --git a/privacysandbox/ui/ui-client/api/restricted_current.txt b/privacysandbox/ui/ui-client/api/restricted_current.txt
index ae23335..bc9a40d 100644
--- a/privacysandbox/ui/ui-client/api/restricted_current.txt
+++ b/privacysandbox/ui/ui-client/api/restricted_current.txt
@@ -1,11 +1,20 @@
// Signature format: 4.0
package androidx.privacysandbox.ui.client {
+ public interface LocalSdkActivityLauncher<T extends android.app.Activity & androidx.lifecycle.LifecycleOwner> extends androidx.privacysandbox.ui.core.SdkActivityLauncher {
+ method public void dispose();
+ }
+
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class SandboxedUiAdapterFactory {
method public androidx.privacysandbox.ui.core.SandboxedUiAdapter createFromCoreLibInfo(android.os.Bundle coreLibInfo);
field public static final androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory INSTANCE;
}
+ public final class SdkActivityLaunchers {
+ method public static <T extends android.app.Activity & androidx.lifecycle.LifecycleOwner> androidx.privacysandbox.ui.client.LocalSdkActivityLauncher<T> createSdkActivityLauncher(T, kotlin.jvm.functions.Function0<java.lang.Boolean> allowLaunch);
+ method public static android.os.Bundle toLauncherInfo(androidx.privacysandbox.ui.core.SdkActivityLauncher);
+ }
+
}
package androidx.privacysandbox.ui.client.view {
diff --git a/privacysandbox/ui/ui-client/build.gradle b/privacysandbox/ui/ui-client/build.gradle
index 747c1d1..1dc67e0 100644
--- a/privacysandbox/ui/ui-client/build.gradle
+++ b/privacysandbox/ui/ui-client/build.gradle
@@ -25,7 +25,12 @@
dependencies {
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.1.0")
+
+ implementation("androidx.core:core:1.7.0")
+ implementation("androidx.lifecycle:lifecycle-common:2.2.0")
+ implementation project(path: ':privacysandbox:sdkruntime:sdkruntime-client')
implementation project(path: ':privacysandbox:ui:ui-core')
+
androidTestImplementation(project(":internal-testutils-runtime"))
androidTestImplementation(libs.junit)
androidTestImplementation(libs.kotlinStdlib)
@@ -34,11 +39,18 @@
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
+ androidTestImplementation(libs.espressoIntents)
+ androidTestImplementation(libs.espressoCore)
+ androidTestImplementation(libs.mockitoCore)
+ androidTestImplementation(libs.multidex)
androidTestImplementation project(path: ':appcompat:appcompat')
}
android {
namespace "androidx.privacysandbox.ui.client"
+ defaultConfig {
+ multiDexEnabled true
+ }
}
androidx {
diff --git a/privacysandbox/ui/ui-client/src/androidTest/AndroidManifest.xml b/privacysandbox/ui/ui-client/src/androidTest/AndroidManifest.xml
index e114f95..b2720e1 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/AndroidManifest.xml
+++ b/privacysandbox/ui/ui-client/src/androidTest/AndroidManifest.xml
@@ -16,6 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:supportsRtl="true"
+ android:name="androidx.multidex.MultiDexApplication"
android:theme="@style/Theme.AppCompat">
<activity
android:name="androidx.privacysandbox.ui.client.test.UiLibActivity"
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/CreateSdkActivityLauncherTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/CreateSdkActivityLauncherTest.kt
new file mode 100644
index 0000000..3b50998
--- /dev/null
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/CreateSdkActivityLauncherTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.client.test
+
+import android.app.Activity
+import android.app.Instrumentation.ActivityResult
+import android.content.Intent
+import android.os.Binder
+import android.os.Build
+import androidx.lifecycle.Lifecycle
+import androidx.privacysandbox.ui.client.createSdkActivityLauncher
+import androidx.test.espresso.intent.Intents.intended
+import androidx.test.espresso.intent.Intents.intending
+import androidx.test.espresso.intent.Intents.times
+import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
+import androidx.test.espresso.intent.rule.IntentsRule
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.hamcrest.Matchers.`is`
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class CreateSdkActivityLauncherTest {
+ @get:Rule
+ var activityScenarioRule = ActivityScenarioRule(UiLibActivity::class.java)
+
+ @get:Rule
+ var intentsRule = IntentsRule()
+
+ private val sdkSandboxActivityMatcher =
+ hasAction(`is`("android.app.sdksandbox.action.START_SANDBOXED_ACTIVITY"))
+
+ @Before
+ fun setUp() {
+ // Intercepts intent to start sandboxed activity and immediately return a result.
+ // This allows us to avoid loading and setting up an SDK just for checking if activities are
+ // launched.
+ intending(sdkSandboxActivityMatcher)
+ .respondWith(ActivityResult(Activity.RESULT_OK, Intent()))
+ }
+
+ @Test
+ fun returnedLauncher_launchesActivitiesWhenAllowed() = runBlocking {
+ val launcher = activityScenarioRule.withActivity { this.createSdkActivityLauncher { true } }
+
+ val result = launcher.launchSdkActivity(Binder())
+
+ assertThat(result).isTrue()
+ intended(sdkSandboxActivityMatcher, times(1))
+ }
+
+ @Test
+ fun returnedLauncher_rejectsActivityLaunchesAccordingToPredicate() = runBlocking {
+ val launcher =
+ activityScenarioRule.withActivity { this.createSdkActivityLauncher { false } }
+
+ val result = launcher.launchSdkActivity(Binder())
+
+ assertThat(result).isFalse()
+ intended(sdkSandboxActivityMatcher, times(0))
+ }
+
+ @Test
+ fun returnedLauncher_rejectsActivityLaunchesWhenDisposed() = runBlocking {
+ val launcher = activityScenarioRule.withActivity { this.createSdkActivityLauncher { true } }
+ launcher.dispose()
+
+ val result = launcher.launchSdkActivity(Binder())
+
+ assertThat(result).isFalse()
+ intended(sdkSandboxActivityMatcher, times(0))
+ }
+
+ @Test
+ fun returnedLauncher_disposeCanBeCalledMultipleTimes() = runBlocking {
+ val launcher = activityScenarioRule.withActivity { this.createSdkActivityLauncher { true } }
+ launcher.dispose()
+
+ val result = launcher.launchSdkActivity(Binder())
+ launcher.dispose()
+ launcher.dispose()
+
+ assertThat(result).isFalse()
+ intended(sdkSandboxActivityMatcher, times(0))
+ }
+
+ @Test
+ fun returnedLauncher_rejectsActivityLaunchesWhenHostActivityIsDestroyed() = runBlocking {
+ val launcher = activityScenarioRule.withActivity { this.createSdkActivityLauncher { true } }
+ activityScenarioRule.scenario.moveToState(Lifecycle.State.DESTROYED)
+
+ val result = launcher.launchSdkActivity(Binder())
+
+ assertThat(result).isFalse()
+ intended(sdkSandboxActivityMatcher, times(0))
+ }
+
+ @Test
+ fun returnedLauncher_rejectsActivityLaunchesWhenHostActivityWasAlreadyDestroyed() =
+ runBlocking {
+ val activity = activityScenarioRule.withActivity { this }
+ activityScenarioRule.scenario.moveToState(Lifecycle.State.DESTROYED)
+ val launcher = activity.createSdkActivityLauncher { true }
+
+ val result = launcher.launchSdkActivity(Binder())
+
+ assertThat(result).isFalse()
+ intended(sdkSandboxActivityMatcher, times(0))
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SdkActivityLaunchers.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SdkActivityLaunchers.kt
new file mode 100644
index 0000000..ef932dd
--- /dev/null
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SdkActivityLaunchers.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("SdkActivityLaunchers")
+
+// TODO(b/282918396): Stop using app.BundleCompat and change it to os.BundleCompat when permission
+// issue is fixed.
+@file:Suppress("DEPRECATION")
+
+package androidx.privacysandbox.ui.client
+
+import android.app.Activity
+import android.os.Bundle
+import android.os.IBinder
+import androidx.core.app.BundleCompat
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
+import androidx.privacysandbox.ui.core.ISdkActivityLauncher
+import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback
+import androidx.privacysandbox.ui.core.ProtocolConstants.sdkActivityLauncherBinderKey
+import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import java.util.concurrent.atomic.AtomicReference
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Returns an SdkActivityLauncher that launches activities on behalf of an SDK by using this
+ * activity as a starting context.
+ *
+ * @param T the current activity from which new SDK activities will be launched. If this activity is
+ * destroyed any further SDK activity launches will simply be ignored.
+ * @param allowLaunch predicate called each time an activity is about to be launched by the
+ * SDK, the activity will only be launched if it returns true.
+ */
+fun <T> T.createSdkActivityLauncher(
+ allowLaunch: () -> Boolean
+): LocalSdkActivityLauncher<T>
+ where T : Activity, T : LifecycleOwner {
+ val cancellationJob = Job(parent = lifecycleScope.coroutineContext[Job])
+ val launcher = LocalSdkActivityLauncherImpl(
+ activity = this,
+ allowLaunch = allowLaunch,
+ onDispose = { cancellationJob.cancel() },
+ )
+ cancellationJob.invokeOnCompletion {
+ launcher.dispose()
+ }
+ return launcher
+}
+
+/**
+ * Returns a [Bundle] with the information necessary to recreate this launcher.
+ * Possibly in a different process.
+ */
+fun SdkActivityLauncher.toLauncherInfo(): Bundle {
+ val binderDelegate = SdkActivityLauncherBinderDelegate(this)
+ return Bundle().also { bundle ->
+ BundleCompat.putBinder(bundle, sdkActivityLauncherBinderKey, binderDelegate)
+ }
+}
+
+/**
+ * Local implementation of an SDK Activity launcher.
+ *
+ * It allows callers in the app process to dispose resources used to launch SDK activities.
+ */
+interface LocalSdkActivityLauncher<T> : SdkActivityLauncher where T : Activity, T : LifecycleOwner {
+ /**
+ * Clears references used to launch activities.
+ *
+ * After this method is called all further attempts to launch activities wil be rejected.
+ * Doesn't do anything if the launcher was already disposed of.
+ */
+ fun dispose()
+}
+
+private class LocalSdkActivityLauncherImpl<T>(
+ activity: T,
+ allowLaunch: () -> Boolean,
+ onDispose: () -> Unit
+) : LocalSdkActivityLauncher<T> where T : Activity, T : LifecycleOwner {
+
+ /** Internal state for [LocalSdkActivityLauncher], cleared when the launcher is disposed. */
+ private class LocalLauncherState<T>(
+ val activity: T,
+ val allowLaunch: () -> Boolean,
+ val sdkSandboxManager: SdkSandboxManagerCompat,
+ val onDispose: () -> Unit
+ ) where T : Activity, T : LifecycleOwner
+
+ private val stateReference: AtomicReference<LocalLauncherState<T>?> =
+ AtomicReference<LocalLauncherState<T>?>(
+ LocalLauncherState(
+ activity,
+ allowLaunch,
+ SdkSandboxManagerCompat.from(activity),
+ onDispose
+ )
+ )
+
+ override suspend fun launchSdkActivity(
+ sdkActivityHandlerToken: IBinder
+ ): Boolean {
+ val state = stateReference.get() ?: return false
+ return withContext(Dispatchers.Main.immediate) {
+ state.run {
+ allowLaunch().also { didAllowLaunch ->
+ if (didAllowLaunch) {
+ sdkSandboxManager.startSdkSandboxActivity(activity, sdkActivityHandlerToken)
+ }
+ }
+ }
+ }
+ }
+
+ override fun dispose() {
+ stateReference.getAndSet(null)?.run {
+ onDispose()
+ }
+ }
+}
+
+private class SdkActivityLauncherBinderDelegate(private val launcher: SdkActivityLauncher) :
+ ISdkActivityLauncher.Stub() {
+
+ private val coroutineScope = CoroutineScope(Dispatchers.Main)
+
+ override fun launchSdkActivity(
+ sdkActivityHandlerToken: IBinder?,
+ callback: ISdkActivityLauncherCallback?
+ ) {
+ requireNotNull(sdkActivityHandlerToken)
+ requireNotNull(callback)
+
+ coroutineScope.launch {
+ val accepted = try {
+ launcher.launchSdkActivity(sdkActivityHandlerToken)
+ } catch (t: Throwable) {
+ callback.onLaunchError(t.message ?: "Unknown error launching SDK activity.")
+ return@launch
+ }
+
+ if (accepted) {
+ callback.onLaunchAccepted(sdkActivityHandlerToken)
+ } else {
+ callback.onLaunchRejected(sdkActivityHandlerToken)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-core/api/current.txt b/privacysandbox/ui/ui-core/api/current.txt
index 4154b4b..db32e1e 100644
--- a/privacysandbox/ui/ui-core/api/current.txt
+++ b/privacysandbox/ui/ui-core/api/current.txt
@@ -20,6 +20,10 @@
method public void onSessionOpened(androidx.privacysandbox.ui.core.SandboxedUiAdapter.Session session);
}
+ public interface SdkActivityLauncher {
+ method public suspend Object? launchSdkActivity(android.os.IBinder sdkActivityHandlerToken, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
public final class SdkRuntimeUiLibVersions {
method public int getClientVersion();
property public final int clientVersion;
diff --git a/privacysandbox/ui/ui-core/api/restricted_current.txt b/privacysandbox/ui/ui-core/api/restricted_current.txt
index 4154b4b..db32e1e 100644
--- a/privacysandbox/ui/ui-core/api/restricted_current.txt
+++ b/privacysandbox/ui/ui-core/api/restricted_current.txt
@@ -20,6 +20,10 @@
method public void onSessionOpened(androidx.privacysandbox.ui.core.SandboxedUiAdapter.Session session);
}
+ public interface SdkActivityLauncher {
+ method public suspend Object? launchSdkActivity(android.os.IBinder sdkActivityHandlerToken, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
public final class SdkRuntimeUiLibVersions {
method public int getClientVersion();
property public final int clientVersion;
diff --git a/privacysandbox/ui/ui-core/lint-baseline.xml b/privacysandbox/ui/ui-core/lint-baseline.xml
index c6aa1c9..23022fb 100644
--- a/privacysandbox/ui/ui-core/lint-baseline.xml
+++ b/privacysandbox/ui/ui-core/lint-baseline.xml
@@ -37,4 +37,22 @@
file="src/main/aidl/androidx/privacysandbox/ui/core/ISandboxedUiAdapter.aidl"/>
</issue>
+ <issue
+ id="RequireUnstableAidlAnnotation"
+ message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
+ errorLine1="oneway interface ISdkActivityLauncher {"
+ errorLine2="^">
+ <location
+ file="src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncher.aidl"/>
+ </issue>
+
+ <issue
+ id="RequireUnstableAidlAnnotation"
+ message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
+ errorLine1="oneway interface ISdkActivityLauncherCallback {"
+ errorLine2="^">
+ <location
+ file="src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncherCallback.aidl"/>
+ </issue>
+
</issues>
diff --git a/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncher.aidl b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncher.aidl
new file mode 100644
index 0000000..76cfa3c
--- /dev/null
+++ b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncher.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.core;
+
+import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback;
+
+/* @hide */
+oneway interface ISdkActivityLauncher {
+ void launchSdkActivity(
+ in IBinder sdkActivityHandlerToken,
+ ISdkActivityLauncherCallback callback) = 1;
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncherCallback.aidl b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncherCallback.aidl
new file mode 100644
index 0000000..22b7beb
--- /dev/null
+++ b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISdkActivityLauncherCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.core;
+
+/* @hide */
+oneway interface ISdkActivityLauncherCallback {
+ void onLaunchAccepted(in IBinder sdkActivityHandlerToken) = 1;
+ void onLaunchRejected(in IBinder sdkActivityHandlerToken) = 2;
+ void onLaunchError(String message) = 3;
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/ProtocolConstants.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/ProtocolConstants.kt
new file mode 100644
index 0000000..5bff1c9
--- /dev/null
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/ProtocolConstants.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.core
+
+import androidx.annotation.RestrictTo
+
+/**
+ * Constants shared between UI library artifacts to establish an IPC protocol across library
+ * versions. Adding new constants is allowed, but **never change the value of a constant**, or
+ * you'll break binary compatibility between UI library versions.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object ProtocolConstants {
+ const val sdkActivityLauncherBinderKey = "sdkActivityLauncherBinderKey"
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SdkActivityLauncher.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SdkActivityLauncher.kt
new file mode 100644
index 0000000..edd3008
--- /dev/null
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SdkActivityLauncher.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.core
+
+import android.os.IBinder
+
+/**
+ * Interface that allows SDKs running in the Privacy Sandbox to launch activities.
+ *
+ * Apps can create launchers by calling
+ * [createActivityLauncher][androidx.privacysandbox.ui.client.createSdkActivityLauncher]
+ * from one of their activities.
+ *
+ * To send an [SdkActivityLauncher] to another process, they can call
+ * [toLauncherInfo][androidx.privacysandbox.ui.client.toLauncherInfo]
+ * and send the resulting bundle.
+ *
+ * SDKs can create launchers from an app-provided bundle by calling
+ * [createFromLauncherInfo][androidx.privacysandbox.ui.provider.SdkActivityLauncherFactory.createFromLauncherInfo].
+ */
+interface SdkActivityLauncher {
+
+ /**
+ * Tries to launch a new SDK activity using the given [sdkActivityHandlerToken],
+ * assumed to be registered in the [SdkSandboxControllerCompat][androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat].
+ *
+ * Returns true if the SDK activity intent was sent, false if the launch was rejected for any
+ * reason.
+ */
+ suspend fun launchSdkActivity(sdkActivityHandlerToken: IBinder): Boolean
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-provider/api/current.txt b/privacysandbox/ui/ui-provider/api/current.txt
index 20170b4..03fbefd 100644
--- a/privacysandbox/ui/ui-provider/api/current.txt
+++ b/privacysandbox/ui/ui-provider/api/current.txt
@@ -5,5 +5,10 @@
method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static android.os.Bundle toCoreLibInfo(androidx.privacysandbox.ui.core.SandboxedUiAdapter, android.content.Context context);
}
+ public final class SdkActivityLauncherFactory {
+ method public static androidx.privacysandbox.ui.core.SdkActivityLauncher fromLauncherInfo(android.os.Bundle launcherInfo);
+ field public static final androidx.privacysandbox.ui.provider.SdkActivityLauncherFactory INSTANCE;
+ }
+
}
diff --git a/privacysandbox/ui/ui-provider/api/restricted_current.txt b/privacysandbox/ui/ui-provider/api/restricted_current.txt
index 20170b4..03fbefd 100644
--- a/privacysandbox/ui/ui-provider/api/restricted_current.txt
+++ b/privacysandbox/ui/ui-provider/api/restricted_current.txt
@@ -5,5 +5,10 @@
method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static android.os.Bundle toCoreLibInfo(androidx.privacysandbox.ui.core.SandboxedUiAdapter, android.content.Context context);
}
+ public final class SdkActivityLauncherFactory {
+ method public static androidx.privacysandbox.ui.core.SdkActivityLauncher fromLauncherInfo(android.os.Bundle launcherInfo);
+ field public static final androidx.privacysandbox.ui.provider.SdkActivityLauncherFactory INSTANCE;
+ }
+
}
diff --git a/privacysandbox/ui/ui-provider/build.gradle b/privacysandbox/ui/ui-provider/build.gradle
index a89f128..3ff3156 100644
--- a/privacysandbox/ui/ui-provider/build.gradle
+++ b/privacysandbox/ui/ui-provider/build.gradle
@@ -25,7 +25,11 @@
dependencies {
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.1.0")
+
implementation project(path: ':privacysandbox:ui:ui-core')
+ implementation("androidx.core:core:1.7.0")
+ implementation(libs.kotlinCoroutinesCore)
+
androidTestImplementation(libs.junit)
androidTestImplementation(libs.kotlinStdlib)
androidTestImplementation(libs.testExtJunit)
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/SdkActivityLauncherFactory.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/SdkActivityLauncherFactory.kt
new file mode 100644
index 0000000..622a46f
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/SdkActivityLauncherFactory.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/282918396): Stop using app.BundleCompat and change it to os.BundleCompat when permission
+// issue is fixed.
+@file:Suppress("DEPRECATION")
+
+package androidx.privacysandbox.ui.provider
+
+import android.os.Bundle
+import android.os.IBinder
+import androidx.core.app.BundleCompat
+import androidx.privacysandbox.ui.core.ISdkActivityLauncher
+import androidx.privacysandbox.ui.core.ISdkActivityLauncherCallback
+import androidx.privacysandbox.ui.core.ProtocolConstants.sdkActivityLauncherBinderKey
+import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+object SdkActivityLauncherFactory {
+
+ /**
+ * Creates a [SdkActivityLauncher] using the given [launcherInfo] Bundle.
+ *
+ * You can create such a Bundle by calling [toLauncherInfo][androidx.privacysandbox.ui.client.toLauncherInfo].
+ * A [launcherInfo] is expected to have a valid SdkActivityLauncher Binder with
+ * `"sdkActivityLauncherBinderKey"` for a key, [IllegalArgumentException] is thrown otherwise.
+ */
+ @JvmStatic
+ fun fromLauncherInfo(launcherInfo: Bundle): SdkActivityLauncher {
+ val remote: ISdkActivityLauncher? = ISdkActivityLauncher.Stub.asInterface(
+ BundleCompat.getBinder(launcherInfo, sdkActivityLauncherBinderKey)
+ )
+ requireNotNull(remote) { "Invalid SdkActivityLauncher info bundle." }
+ return SdkActivityLauncherProxy(remote)
+ }
+
+ private class SdkActivityLauncherProxy(
+ private val remote: ISdkActivityLauncher
+ ) : SdkActivityLauncher {
+ override suspend fun launchSdkActivity(sdkActivityHandlerToken: IBinder): Boolean =
+ suspendCancellableCoroutine {
+ remote.launchSdkActivity(
+ sdkActivityHandlerToken,
+ object : ISdkActivityLauncherCallback.Stub() {
+ override fun onLaunchAccepted(sdkActivityHandlerToken: IBinder?) {
+ it.resume(true)
+ }
+
+ override fun onLaunchRejected(sdkActivityHandlerToken: IBinder?) {
+ it.resume(false)
+ }
+
+ override fun onLaunchError(message: String?) {
+ it.resumeWithException(RuntimeException(message))
+ }
+ })
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-tests/build.gradle b/privacysandbox/ui/ui-tests/build.gradle
index a675a27..cf66642 100644
--- a/privacysandbox/ui/ui-tests/build.gradle
+++ b/privacysandbox/ui/ui-tests/build.gradle
@@ -26,12 +26,14 @@
implementation project(path: ':privacysandbox:ui:ui-core')
implementation project(path: ':privacysandbox:ui:ui-client')
implementation project(path: ':privacysandbox:ui:ui-provider')
+ implementation(libs.kotlinStdlib)
+
androidTestImplementation(project(":internal-testutils-runtime"))
- api(libs.kotlinStdlib)
androidTestImplementation(libs.junit)
androidTestImplementation(libs.kotlinStdlib)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.kotlinCoroutinesCore)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/activity/SdkActivityLauncherBundlingTest.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/activity/SdkActivityLauncherBundlingTest.kt
new file mode 100644
index 0000000..6971f7c
--- /dev/null
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/activity/SdkActivityLauncherBundlingTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.tests.activity
+
+import android.os.Binder
+import android.os.IBinder
+import androidx.privacysandbox.ui.client.toLauncherInfo
+import androidx.privacysandbox.ui.core.SdkActivityLauncher
+import androidx.privacysandbox.ui.provider.SdkActivityLauncherFactory
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkActivityLauncherBundlingTest {
+
+ @Test
+ fun unbundledSdkActivityLauncher_launchesActivities(): Unit = runBlocking {
+ val launcher = TestSdkActivityLauncher()
+ val launcherInfo = launcher.toLauncherInfo()
+
+ val unbundledLauncher = SdkActivityLauncherFactory.fromLauncherInfo(launcherInfo)
+ val token = Binder()
+ val result = unbundledLauncher.launchSdkActivity(token)
+
+ assertThat(result).isTrue()
+ assertThat(launcher.tokensReceived).containsExactly(token)
+ }
+
+ @Test
+ fun unbundledSdkActivityLauncher_rejectsActivityLaunches(): Unit = runBlocking {
+ val launcher = TestSdkActivityLauncher()
+ launcher.allowActivityLaunches = false
+ val launcherInfo = launcher.toLauncherInfo()
+
+ val unbundledLauncher = SdkActivityLauncherFactory.fromLauncherInfo(launcherInfo)
+ val token = Binder()
+ val result = unbundledLauncher.launchSdkActivity(token)
+
+ assertThat(result).isFalse()
+ assertThat(launcher.tokensReceived).containsExactly(token)
+ }
+
+ class TestSdkActivityLauncher : SdkActivityLauncher {
+ var allowActivityLaunches: Boolean = true
+
+ var tokensReceived = mutableListOf<IBinder>()
+
+ override suspend fun launchSdkActivity(sdkActivityHandlerToken: IBinder):
+ Boolean {
+ tokensReceived.add(sdkActivityHandlerToken)
+ return allowActivityLaunches
+ }
+ }
+}
\ No newline at end of file
diff --git a/recyclerview/recyclerview/api/api_lint.ignore b/recyclerview/recyclerview/api/api_lint.ignore
index ee7fa16..463599f 100644
--- a/recyclerview/recyclerview/api/api_lint.ignore
+++ b/recyclerview/recyclerview/api/api_lint.ignore
@@ -161,12 +161,6 @@
Internal field mLayoutManager must not be exposed
-InvalidNullabilityOverride: androidx.recyclerview.widget.RecyclerView#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.recyclerview.widget.RecyclerView#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
- Invalid nullability on parameter `canvas` in method `drawChild`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.recyclerview.widget.RecyclerView#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate.ItemDelegate#dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent) parameter #0:
Invalid nullability on parameter `host` in method `dispatchPopulateAccessibilityEvent`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate.ItemDelegate#dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent) parameter #1:
@@ -551,6 +545,10 @@
Missing nullability on parameter `container` in method `dispatchRestoreInstanceState`
MissingNullability: androidx.recyclerview.widget.RecyclerView#dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>) parameter #0:
Missing nullability on parameter `container` in method `dispatchSaveInstanceState`
+MissingNullability: androidx.recyclerview.widget.RecyclerView#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `draw`
+MissingNullability: androidx.recyclerview.widget.RecyclerView#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
+ Missing nullability on parameter `canvas` in method `drawChild`
MissingNullability: androidx.recyclerview.widget.RecyclerView#drawChild(android.graphics.Canvas, android.view.View, long) parameter #1:
Missing nullability on parameter `child` in method `drawChild`
MissingNullability: androidx.recyclerview.widget.RecyclerView#findViewHolderForItemId(long):
@@ -573,6 +571,8 @@
Missing nullability on method `getAccessibilityClassName` return
MissingNullability: androidx.recyclerview.widget.RecyclerView#getChildViewHolder(android.view.View):
Missing nullability on method `getChildViewHolder` return
+MissingNullability: androidx.recyclerview.widget.RecyclerView#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `onDraw`
MissingNullability: androidx.recyclerview.widget.RecyclerView#onGenericMotionEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `event` in method `onGenericMotionEvent`
MissingNullability: androidx.recyclerview.widget.RecyclerView#onInterceptTouchEvent(android.view.MotionEvent) parameter #0:
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 8c7e013..2f52af1 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -8,7 +8,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api "androidx.core:core:1.7.0"
+ api(project(":core:core"))
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
implementation("androidx.customview:customview-poolingcontainer:1.0.0")
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/GridLayoutManagerTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/GridLayoutManagerTest.java
index a1b18f0..efca5df 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/GridLayoutManagerTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/GridLayoutManagerTest.java
@@ -19,9 +19,14 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_ACCESSIBILITY_FOCUS;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION;
+import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
import static androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL;
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -30,20 +35,25 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.UiAutomation;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.util.Pair;
import android.util.SparseIntArray;
import android.util.StateSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.GridView;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.test.annotation.UiThreadTest;
@@ -51,8 +61,6 @@
import androidx.test.filters.LargeTest;
import androidx.test.filters.SdkSuppress;
-import com.google.common.truth.Truth;
-
import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,6 +69,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
@LargeTest
@@ -68,6 +77,9 @@
public class GridLayoutManagerTest extends BaseGridLayoutManagerTest {
private static final int[] SPAN_SIZES = new int[]{1, 1, 1, 2, 2, 2, 2, 3, 3, 2, 2, 2};
+
+ private static final int DEFAULT_ACCESSIBILITY_EVENT_TIMEOUT_MILLIS = 5000;
+
private final GridLayoutManager.SpanSizeLookup mSpanSizeLookupForSpanIndexTest =
new GridLayoutManager.SpanSizeLookup() {
@Override
@@ -964,8 +976,7 @@
waitForFirstLayout(recyclerView);
final AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain();
- assertFalse(nodeInfo.getActionList().contains(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION));
+ assertFalse(nodeInfo.getActionList().contains(ACTION_SCROLL_TO_POSITION));
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -973,8 +984,7 @@
}
});
- assertFalse(nodeInfo.getActionList().contains(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION));
+ assertFalse(nodeInfo.getActionList().contains(ACTION_SCROLL_TO_POSITION));
}
@Test
@@ -985,8 +995,7 @@
waitForFirstLayout(recyclerView);
final AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain();
- assertFalse(nodeInfo.getActionList().contains(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION));
+ assertFalse(nodeInfo.getActionList().contains(ACTION_SCROLL_TO_POSITION));
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -994,8 +1003,7 @@
}
});
- assertTrue(nodeInfo.getActionList().contains(
- AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION));
+ assertTrue(nodeInfo.getActionList().contains(ACTION_SCROLL_TO_POSITION));
}
@Test
@@ -1102,6 +1110,935 @@
assertEquals(((TextView) mGlm.getChildAt(0)).getText(), "Item (6)");
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ public void onInitializeAccessibilityNodeInfo_addActionScrollInDirection_notAddedWithEmptyList()
+ throws Throwable {
+ mRecyclerView = setupBasic(new Config(2, 0));
+ final AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain();
+
+ assertThat(nodeInfo.getActionList()).doesNotContain(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+
+ mActivityRule.runOnUiThread(
+ () -> mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(nodeInfo));
+
+ assertThat(nodeInfo.getActionList()).doesNotContain(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ public void onInitializeAccessibilityNodeInfo_addActionScrollInDirection_withOneItemList()
+ throws Throwable {
+ mRecyclerView = setupBasic(new Config(2, 1));
+ final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
+
+ assertThat(node.getActionList()).doesNotContain(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+
+ mActivityRule.runOnUiThread(
+ () -> mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(node));
+
+ assertThat(node.getActionList()).doesNotContain(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ public void onInitializeAccessibilityNodeInfo_addActionScrollInDirection_withMoreThanOneItem()
+ throws Throwable {
+
+ mRecyclerView = setupBasic(new Config(2, 2));
+ final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
+
+ assertThat(node.getActionList()).doesNotContain(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+
+ mActivityRule.runOnUiThread(
+ () -> mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(node));
+
+ assertThat(node.getActionList()).contains(
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_withoutSpecifyingDirection()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(6, HORIZONTAL);
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ final boolean[] returnValue = {false};
+ mActivityRule.runOnUiThread(
+ () -> {
+ returnValue[0] = mRecyclerView.getLayoutManager().performAccessibilityAction(
+ ACTION_SCROLL_IN_DIRECTION.getId(), null);
+ });
+ assertThat(returnValue[0]).isFalse();
+ assertThat(mGlm.mRowWithAccessibilityFocus).isEqualTo(-1);
+ assertThat(mGlm.mColumnWithAccessibilityFocus).isEqualTo(-1);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_withInvalidDirection()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(6, HORIZONTAL);
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndFail(-1, Pair.create(-1, -1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_withoutSettingAccessibilityFocus()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ // Return value of this call is not used.
+ setUpGridLayoutManagerAccessibilityTest(6, HORIZONTAL);
+ runScrollInDirectionAndFail(View.FOCUS_RIGHT, Pair.create(-1, -1));
+
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_vertical_scrollTargetOnTheSameRow()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (3)",
+ Pair.create(0, 2));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_vertical_scrollTargetOnTheNextRow()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 5
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (4)",
+ Pair.create(1, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_vertical_traversingThroughASpan()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ mRecyclerView = setupBasic(new Config(4, 4));
+ mGlm.setOrientation(VERTICAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 1) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 2 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (2)" ,
+ Pair.create(0, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (3)" ,
+ Pair.create(0, 3));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_vertical_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 5
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(4));
+ runScrollInDirectionAndFail(View.FOCUS_RIGHT, Pair.create(1, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_horizontal_scrollTargetOnTheSameRow()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (4)" ,
+ Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_horizontal_traversingThroughASpan()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(8, HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 4) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 4 6
+ 2 5 7
+ 3 5 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (5)" ,
+ Pair.create(2, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(4));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (8)" ,
+ Pair.create(2, 2));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_horizontal_withWrapAround()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(8, HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 0) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 3 6
+ 1 4 7
+ 2 5 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(5));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (1)" ,
+ Pair.create(1, 0));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_RIGHT, "Item (4)" ,
+ Pair.create(1, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusRight_horizontal_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndFail(View.FOCUS_RIGHT);
+ assertThat(mGlm.mRowWithAccessibilityFocus).isEqualTo(2);
+ assertThat(mGlm.mColumnWithAccessibilityFocus).isEqualTo(0);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_vertical_scrollTargetOnTheSameRow()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (1)",
+ Pair.create(0, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_vertical_scrollTargetOnAPreviousRow()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 5
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (3)",
+ Pair.create(0, 2));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_vertical_traversingThroughASpan()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(4, VERTICAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 1) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 2 2
+ 3 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (2)",
+ Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_vertical_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 5
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndFail(View.FOCUS_LEFT, Pair.create(0, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_horizontal_scrollTargetOnTheSameRow()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(4));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (2)" ,
+ Pair.create(1, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_horizontal_traversingThroughASpan()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(8, HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 4) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 4 6
+ 2 5 7
+ 3 5 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(7));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (5)" ,
+ Pair.create(2, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(4));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (3)" ,
+ Pair.create(2, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_horizontal_withWrapAround()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(8, HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 6) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 4 7
+ 2 5 7
+ 3 6 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (7)" ,
+ Pair.create(1, 2));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(6));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_LEFT, "Item (5)" ,
+ Pair.create(1, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusLeft_horizontal_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndFail(View.FOCUS_LEFT, Pair.create(0, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_vertical_scrollTargetOnTheSameColumn()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (1)",
+ Pair.create(0, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_vertical_traversingThroughASpan()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ mRecyclerView = setupBasic(new Config(3, 8));
+ mGlm.setOrientation(VERTICAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 3) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 4 5
+ 6 7 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(6));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (4)" ,
+ Pair.create(1, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (2)" ,
+ Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_vertical_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndFail(View.FOCUS_UP, Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_horizontal_scrollTargetOnTheSameColumn()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (1)" ,
+ Pair.create(0, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_horizontal_traversingThroughASpan()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ mRecyclerView = setupBasic(new Config(4, 9));
+ mGlm.setOrientation(HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 5) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 5 8
+ 2 6 9
+ 3 6
+ 4 7
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(6));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (6)" ,
+ Pair.create(2, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(5));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_UP, "Item (5)" ,
+ Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusUp_horizontal_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndFail(View.FOCUS_UP, Pair.create(0, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_vertical_scrollTargetOnTheSameColumn()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(0));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (4)", Pair.create(1,
+ 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_vertical_traversingThroughASpan()
+ throws Throwable {
+
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ mRecyclerView = setupBasic(new Config(3, 8));
+ mGlm.setOrientation(VERTICAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 3) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4 4 5
+ 6 7 8
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (4)" ,
+ Pair.create(1, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (7)" ,
+ Pair.create(2, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_vertical_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android version.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(4, VERTICAL);
+ /*
+ This generates the following grid:
+ 1 2 3
+ 4
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndFail(View.FOCUS_DOWN, Pair.create(0, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(3));
+ runScrollInDirectionAndFail(View.FOCUS_DOWN, Pair.create(1, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_horizontal_scrollTargetOnTheSameColumn()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(1));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (3)" ,
+ Pair.create(2, 0));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_horizontal_traversingThroughASpan()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ mRecyclerView = setupBasic(new Config(4, 9));
+ mGlm.setOrientation(HORIZONTAL);
+ mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ if (position == 5) {
+ return 2;
+ }
+ return 1;
+ }
+ });
+ waitForFirstLayout(mRecyclerView);
+ /*
+ This generates the following grid:
+ 1 5 8
+ 2 6 9
+ 3 6
+ 4 7
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(4));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (6)" ,
+ Pair.create(1, 1));
+
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(5));
+ runScrollInDirectionAndSucceed(uiAutomation, View.FOCUS_DOWN, "Item (7)" ,
+ Pair.create(3, 1));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void performActionScrollInDirection_focusDown_horizontal_withoutAvailableTarget()
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpGridLayoutManagerAccessibilityTest(5, HORIZONTAL);
+ /*
+ This generates the following grid:
+ 1 4
+ 2 5
+ 3
+ */
+ setAccessibilityFocus(uiAutomation, mGlm.getChildAt(2));
+ runScrollInDirectionAndFail(View.FOCUS_DOWN, Pair.create(2, 0));
+ }
+
+ /**
+ * Verifies that a scroll successfully occurs in the specified {@code direction}.
+ *
+ * @param uiAutomation UiAutomation instance.
+ * @param direction The direction of the scroll.
+ * @param scrollTargetText The text of the view targeted by the scroll.
+ * @throws TimeoutException Exception thrown when an action times out.
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private void runScrollInDirectionAndSucceed(UiAutomation uiAutomation, int direction,
+ String scrollTargetText)
+ throws TimeoutException {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final boolean[] returnValue = {false};
+ AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
+ () -> mActivityRule.runOnUiThread(() -> {
+ returnValue[0] =
+ mRecyclerView.getLayoutManager().performAccessibilityAction(
+ ACTION_SCROLL_IN_DIRECTION.getId(),
+ bundleWithDirectionArg(direction));
+ }),
+ event -> event.getEventType() == AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL,
+ DEFAULT_ACCESSIBILITY_EVENT_TIMEOUT_MILLIS);
+
+ assertThat(scrollTargetText).isEqualTo(awaitedEvent.getSource().getText());
+ assertThat(returnValue[0]).isTrue();
+ }
+
+ /**
+ * Verifies that a scroll successfully occurs in the specified {@code direction} and that the
+ * values of {@code mRowIndexForAccessibility} and {@code mColumnIndexForAccessibility} are
+ * currectly set.
+ *
+ * @param uiAutomation UiAutomation instance.
+ * @param direction The direction of the scroll.
+ * @param scrollTargetText The text of the view targeted by the scroll.
+ * @param rowColumn The values for the row and column with accessibility focus.
+ *
+ * @throws TimeoutException Exception thrown when an action times out.
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private void runScrollInDirectionAndSucceed(UiAutomation uiAutomation, int direction,
+ String scrollTargetText, Pair<Integer, Integer> rowColumn)
+ throws TimeoutException {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ runScrollInDirectionAndSucceed(uiAutomation, direction, scrollTargetText);
+ assertThat(mGlm.mRowWithAccessibilityFocus).isEqualTo(rowColumn.first);
+ assertThat(mGlm.mColumnWithAccessibilityFocus).isEqualTo(rowColumn.second);
+ }
+
+ /**
+ * Verifies that a scroll does not occur in the specified {@code direction}.
+ *
+ * @param direction The direction of the scroll.
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private void runScrollInDirectionAndFail(int direction) {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final boolean[] returnValue = {false};
+
+ mActivityRule.runOnUiThread(
+ () -> {
+ returnValue[0] = mRecyclerView.getLayoutManager().performAccessibilityAction(
+ ACTION_SCROLL_IN_DIRECTION.getId(), bundleWithDirectionArg(direction));
+ });
+
+ assertThat(returnValue[0]).isFalse();
+ }
+
+ /**
+ * Verifies that a scroll does not occur in the specified {@code direction}.
+ *
+ * @param direction The direction of the scroll.
+ * @param rowColumn The values for the row and column with accessibility focus.
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private void runScrollInDirectionAndFail(int direction, Pair<Integer, Integer> rowColumn) {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final boolean[] returnValue = {false};
+
+ mActivityRule.runOnUiThread(
+ () -> {
+ returnValue[0] = mRecyclerView.getLayoutManager().performAccessibilityAction(
+ ACTION_SCROLL_IN_DIRECTION.getId(), bundleWithDirectionArg(direction));
+ });
+
+ assertThat(returnValue[0]).isFalse();
+ assertThat(mGlm.mRowWithAccessibilityFocus).isEqualTo(rowColumn.first);
+ assertThat(mGlm.mColumnWithAccessibilityFocus).isEqualTo(rowColumn.second);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @NonNull
+ private UiAutomation setUpGridLayoutManagerAccessibilityTest(int itemCount, int orientation)
+ throws Throwable {
+ // TODO(b/267511848): suppress to LOLLIPOP once U constants are finalized and available in
+ // earlier android versions.
+
+ final UiAutomation uiAutomation = setUpAndReturnUiAutomation();
+ setUpRecyclerViewAndGridLayoutManager(itemCount, orientation);
+ waitForFirstLayout(mRecyclerView);
+ return uiAutomation;
+ }
+
+ private Bundle bundleWithDirectionArg(int direction) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_DIRECTION_INT, direction);
+ return bundle;
+ }
+
+ @NonNull
+ @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+ private UiAutomation setUpAndReturnUiAutomation() {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ final AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+ uiAutomation.setServiceInfo(info);
+ return uiAutomation;
+ }
+
+ private void setAccessibilityFocus(UiAutomation uiAutomation, View source)
+ throws TimeoutException {
+ AccessibilityEvent awaitedEvent = null;
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ awaitedEvent = uiAutomation.executeAndWaitForEvent(
+ () -> {
+ try {
+ mActivityRule.runOnUiThread(() -> source.performAccessibilityAction(
+ ACTION_ACCESSIBILITY_FOCUS.getId(), null));
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ },
+ event -> event.getEventType()
+ == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ DEFAULT_ACCESSIBILITY_EVENT_TIMEOUT_MILLIS);
+ assertThat(awaitedEvent.getSource().isAccessibilityFocused()).isTrue();
+ }
+ }
+
+ private void setUpRecyclerViewAndGridLayoutManager(int itemCount, int orientation)
+ throws Throwable {
+ mRecyclerView = setupBasic(new Config(3, itemCount));
+ mGlm.setOrientation(orientation);
+ }
+
public GridLayoutManager.LayoutParams ensureGridLp(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
GridLayoutManager.LayoutParams glp;
@@ -1638,7 +2575,7 @@
rv.setLayoutParams(new ViewGroup.LayoutParams(500, 500));
mAdapter.setFullSpan(0);
waitForFirstLayout(rv);
- Truth.assertThat(getPositionToSpanIndexMapping()).containsExactly(
+ assertThat(getPositionToSpanIndexMapping()).containsExactly(
0, 0,
1, 0,
2, 1,
@@ -1656,7 +2593,7 @@
}
});
waitForAnimations(10);
- Truth.assertThat(getPositionToSpanIndexMapping()).containsExactly(
+ assertThat(getPositionToSpanIndexMapping()).containsExactly(
0, 0,
1, 0,
2, 1,
@@ -1669,7 +2606,7 @@
// 3 4
// 5 6
// 7
- Truth.assertThat(getPositionToSpanIndexMapping()).containsExactly(
+ assertThat(getPositionToSpanIndexMapping()).containsExactly(
3, 0,
4, 1,
5, 0,
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityTest.java
index 6eeb355..a6d44ad 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityTest.java
@@ -19,9 +19,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import android.os.Build;
+import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -33,12 +35,17 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.test.filters.SmallTest;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -220,7 +227,7 @@
hScrolledFwd.set(false);
vScrolledFwd.set(false);
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
assertEquals(mHorizontalScrollBefore, hScrolledBack.get());
assertEquals(mVerticalScrollBefore, vScrolledBack.get());
assertEquals(false, hScrolledFwd.get());
@@ -231,7 +238,7 @@
hScrolledFwd.set(false);
vScrolledFwd.set(false);
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
assertEquals(false, hScrolledBack.get());
assertEquals(false, vScrolledBack.get());
assertEquals(mHorizontalScrollAfter, hScrolledFwd.get());
@@ -293,64 +300,8 @@
AtomicInteger hScrolledOffset = new AtomicInteger(0);
AtomicInteger vScrolledOffset = new AtomicInteger(0);
- final RecyclerView recyclerView = new RecyclerView(getActivity()) {
- @Override
- void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator,
- int duration, boolean withNestedScrolling) {
- // Overrides duration to 0 to stop segmentation to get the complete scroll distance.
- int overrideDuration = 0;
- super.smoothScrollBy(dx, dy, interpolator, overrideDuration, withNestedScrolling);
- }
-
- @Override
- public boolean canScrollHorizontally(int direction) {
- return true;
- }
-
- @Override
- public boolean canScrollVertically(int direction) {
- return true;
- }
- };
- final TestAdapter adapter = new TestAdapter(10);
-
- recyclerView.setAdapter(adapter);
- recyclerView.setLayoutManager(new TestLayoutManager() {
- @Override
- public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
- layoutRange(recycler, 0, 5);
- }
-
- @Override
- public RecyclerView.LayoutParams generateDefaultLayoutParams() {
- return new RecyclerView.LayoutParams(-1, -1);
- }
-
- @Override
- public boolean canScrollVertically() {
- return true;
- }
-
- @Override
- public boolean canScrollHorizontally() {
- return true;
- }
-
- @Override
- public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- hScrolledOffset.set(dx);
- return 0;
- }
-
- @Override
- public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- vScrolledOffset.set(dy);
- return 0;
- }
- });
- setRecyclerView(recyclerView);
+ final RecyclerView recyclerView =
+ setUpAndReturnRecyclerViewForScrollActionTest(hScrolledOffset, vScrolledOffset);
final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
.getCompatAccessibilityDelegate();
final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
@@ -371,12 +322,12 @@
int width = recyclerView.getWidth();
int height = recyclerView.getHeight();
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
assertEquals(-width, hScrolledOffset.get());
assertEquals(-height, vScrolledOffset.get());
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
assertEquals(width, hScrolledOffset.get());
assertEquals(height, vScrolledOffset.get());
@@ -392,12 +343,12 @@
getInstrumentation().waitForIdleSync();
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
assertEquals(-halfWidth, hScrolledOffset.get());
assertEquals(-halfHeight, vScrolledOffset.get());
performAccessibilityAction(delegateCompat, recyclerView,
- AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
assertEquals(halfWidth, hScrolledOffset.get());
assertEquals(halfHeight, vScrolledOffset.get());
} finally {
@@ -407,13 +358,259 @@
}
}
+ @Ignore("b/283754680")
+ @Test
+ public void performGranularScrolling_changesTheScrollAmount()
+ throws Throwable {
+ AtomicInteger hScrolledOffset = new AtomicInteger(0);
+ AtomicInteger vScrolledOffset = new AtomicInteger(0);
+ final RecyclerView recyclerView = setUpAndReturnRecyclerViewForScrollActionTest(
+ hScrolledOffset, vScrolledOffset);
+ final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
+ .getCompatAccessibilityDelegate();
+ final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+
+ mActivityRule.runOnUiThread(
+ () -> delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info));
+ getInstrumentation().waitForIdleSync();
+
+ assertTrue(info.isGranularScrollingSupported());
+
+ int width = recyclerView.getWidth();
+ int height = recyclerView.getHeight();
+ Bundle args = new Bundle();
+ float scrollAmount = .5F;
+ args.putFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT,
+ scrollAmount);
+
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, args);
+ int w = (int) (width * scrollAmount);
+ int h = (int) (height * scrollAmount);
+ assertEquals(-w, hScrolledOffset.get());
+ assertEquals(-h, vScrolledOffset.get());
+
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, args);
+ w = (int) (width * scrollAmount);
+ h = (int) (height * scrollAmount);
+ assertEquals(w, hScrolledOffset.get());
+ assertEquals(h, vScrolledOffset.get());
+ }
+
+ @Ignore("b/283754680")
+ @Test
+ public void performGranularScrolling_withDefaultOrUndefinedValues_scrollsByOneScreen()
+ throws Throwable {
+ AtomicInteger hScrolledOffset = new AtomicInteger(0);
+ AtomicInteger vScrolledOffset = new AtomicInteger(0);
+ final RecyclerView recyclerView = setUpAndReturnRecyclerViewForScrollActionTest(
+ hScrolledOffset, vScrolledOffset);
+ final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
+ .getCompatAccessibilityDelegate();
+ final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+
+ mActivityRule.runOnUiThread(
+ () -> delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info));
+ getInstrumentation().waitForIdleSync();
+
+ assertTrue(info.isGranularScrollingSupported());
+
+ int width = recyclerView.getWidth();
+ int height = recyclerView.getHeight();
+ Bundle args = new Bundle();
+ args.putFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT, 1F);
+
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, args);
+ assertEquals(-width, hScrolledOffset.get());
+ assertEquals(-height, vScrolledOffset.get());
+
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, args);
+ assertEquals(width, hScrolledOffset.get());
+ assertEquals(height, vScrolledOffset.get());
+
+ args.putFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT, 0F);
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, args);
+ assertEquals(-width, hScrolledOffset.get());
+ assertEquals(-height, vScrolledOffset.get());
+
+ performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, args);
+ assertEquals(width, hScrolledOffset.get());
+ assertEquals(height, vScrolledOffset.get());
+ }
+
+ @Test
+ public void performGranularScrolling_withANegativeValue_throwsException()
+ throws Throwable {
+
+ final RecyclerView recyclerView = setUpAndReturnRecyclerViewForScrollActionTest(
+ new AtomicInteger(0),
+ new AtomicInteger(0));
+ final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
+ .getCompatAccessibilityDelegate();
+ final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+
+ mActivityRule.runOnUiThread(
+ () -> delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info));
+ getInstrumentation().waitForIdleSync();
+
+ Bundle args = new Bundle();
+ args.putFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT, -1F);
+
+ // Note: this assumes that debug assertions are enabled in tests (but not in production).
+ assertThrows(IllegalArgumentException.class,
+ () -> performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, args));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> performAccessibilityAction(delegateCompat, recyclerView,
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, args));
+ }
+
+ @Test
+ public void performScrollActions_withGranularScrollingForwardToInfinity_scrollsToTheEnd()
+ throws Throwable {
+ final CountDownLatch scrollListenerLatch = new CountDownLatch(1);
+ final RecyclerView recyclerView = createRecyclerViewAndAdapter();
+ setLayoutManagerWithSmoothScrollToPosition(recyclerView, scrollListenerLatch,
+ recyclerView.mAdapter.getItemCount() - 1);
+ setRecyclerView(recyclerView);
+ runScrollToInfinityTest(recyclerView, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD,
+ scrollListenerLatch);
+ }
+
+ @Test
+ public void performScrollActions_withGranularScrollingBackwardToInfinity_scrollsToTheBeginning()
+ throws Throwable {
+ final CountDownLatch scrollListenerLatch = new CountDownLatch(1);
+ final RecyclerView recyclerView = createRecyclerViewAndAdapter();
+ setLayoutManagerWithSmoothScrollToPosition(recyclerView, scrollListenerLatch, 0);
+ setRecyclerView(recyclerView);
+ runScrollToInfinityTest(recyclerView, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD,
+ scrollListenerLatch);
+ }
+
+ private void runScrollToInfinityTest(RecyclerView recyclerView, int action,
+ CountDownLatch scrollListenerLatch) throws Throwable {
+ final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
+ .getCompatAccessibilityDelegate();
+ final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+
+ mActivityRule.runOnUiThread(
+ () -> delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info));
+ getInstrumentation().waitForIdleSync();
+
+ Bundle args = new Bundle();
+ args.putFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT,
+ Float.POSITIVE_INFINITY);
+
+ boolean result = performAccessibilityAction(delegateCompat, recyclerView, action, args);
+
+ assertTrue(result);
+ MatcherAssert.assertThat(scrollListenerLatch.await(2, TimeUnit.SECONDS),
+ CoreMatchers.is(true));
+ }
+
+ private RecyclerView setUpAndReturnRecyclerViewForScrollActionTest(
+ AtomicInteger hScrolledOffset, AtomicInteger vScrolledOffset) throws Throwable {
+ final RecyclerView recyclerView = createRecyclerViewAndAdapter();
+ setLayoutManager(recyclerView, hScrolledOffset, vScrolledOffset);
+ setRecyclerView(recyclerView);
+ return recyclerView;
+ }
+
+ private RecyclerView createRecyclerViewAndAdapter() {
+ final RecyclerView recyclerView = new RecyclerView(getActivity()) {
+ @Override
+ void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator,
+ int duration, boolean withNestedScrolling) {
+ // Overrides duration to 0 to stop segmentation to get the complete scroll distance.
+ int overrideDuration = 0;
+ super.smoothScrollBy(dx, dy, interpolator, overrideDuration, withNestedScrolling);
+ }
+
+ @Override
+ public boolean canScrollHorizontally(int direction) {
+ return true;
+ }
+
+ @Override
+ public boolean canScrollVertically(int direction) {
+ return true;
+ }
+ };
+
+ final TestAdapter adapter = new TestAdapter(100);
+ recyclerView.setAdapter(adapter);
+ return recyclerView;
+ }
+
+ private void setLayoutManager(RecyclerView recyclerView, AtomicInteger hScrolledOffset,
+ AtomicInteger vScrolledOffset) {
+ recyclerView.setLayoutManager(new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ layoutRange(recycler, 0, 100);
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+ return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
+ }
+
+ @Override
+ public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ hScrolledOffset.set(dx);
+ return 0;
+ }
+
+ @Override
+ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ vScrolledOffset.set(dy);
+ return 0;
+ }
+ });
+ }
+
+ private void setLayoutManagerWithSmoothScrollToPosition(RecyclerView recyclerView,
+ CountDownLatch scrollListenerLatch, int scrollTargetPosition) {
+ recyclerView.setLayoutManager(new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ layoutRange(recycler, 0, 100);
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+ return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
+ }
+
+ @Override
+ public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
+ int position) {
+ // RecyclerView delegates the specifics of smooth scrolling to its layout
+ // managers. Here, we don't check for any specific scroll behavior; instead, we just
+ // verify that this method runs with the specified scroll target position.
+ if (position == scrollTargetPosition) {
+ scrollListenerLatch.countDown();
+ }
+ }
+ });
+ }
+
boolean performAccessibilityAction(final AccessibilityDelegateCompat delegate,
- final RecyclerView recyclerView, final int action) throws Throwable {
+ final RecyclerView recyclerView, final int action, final Bundle args) throws Throwable {
final boolean[] result = new boolean[1];
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
- result[0] = delegate.performAccessibilityAction(recyclerView, action, null);
+ result[0] = delegate.performAccessibilityAction(recyclerView, action, args);
}
});
getInstrumentation().waitForIdleSync();
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java
index f043bf6..7bf62a9 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java
@@ -17,19 +17,31 @@
import android.content.Context;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.GridView;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
/**
* A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
@@ -42,6 +54,14 @@
private static final boolean DEBUG = false;
private static final String TAG = "GridLayoutManager";
public static final int DEFAULT_SPAN_COUNT = -1;
+ private static final int INVALID_POSITION = -1;
+
+ private static final Set<Integer> sSupportedDirectionsForActionScrollInDirection =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ View.FOCUS_LEFT,
+ View.FOCUS_RIGHT,
+ View.FOCUS_UP,
+ View.FOCUS_DOWN)));
/**
* Span size have been changed but we've not done a new layout calculation.
@@ -67,6 +87,43 @@
private boolean mUsingSpansToEstimateScrollBarDimensions;
/**
+ * Used to track the position of the target node brought on screen by
+ * {@code ACTIONS_SCROLL_IN_DIRECTION} so that a {@code TYPE_VIEW_TARGETED_BY_SCROLL} event can
+ * be emitted.
+ */
+ private int mPositionTargetedByScrollInDirection = INVALID_POSITION;
+
+ /**
+ * Stores the index of the row with accessibility focus for use with
+ * {@link AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}.
+ * This may include a position that is spanned by a grid child. For example, in the following
+ * grid...
+ * 0 3 4
+ * 1 3 5
+ * 2 3 6
+ * ...the child at adapter position 3 (which spans three rows) could have a row index of either
+ * 0, 1, or 2, and the choice may depend on which row of the grid previously had
+ * accessibility focus. Note that for single span cells, the row index stored here should be
+ * the same as the value returned by {@code getRowIndex()}.
+ */
+ int mRowWithAccessibilityFocus = INVALID_POSITION;
+
+ /**
+ * Stores the index of the column with accessibility focus for use with
+ * {@link AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_SCROLL_IN_DIRECTION}.
+ * This may include a position that is spanned by a grid child. For example, in the following
+ * grid...
+ * 0 1 2
+ * 3 3 3
+ * 4 5 6
+ * ... the child at adapter position 3 (which spans three columns) could have a column index
+ * of either 0, 1, or 2, and the choice may depend on which column of the grid previously had
+ * accessibility focus. Note that for single span cells, the column index stored here should be
+ * the same as the value returned by {@code getColumnIndex()}.
+ */
+ int mColumnWithAccessibilityFocus = INVALID_POSITION;
+
+ /**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". If spanCount is not specified in the XML, it defaults to a
* single column.
@@ -175,11 +232,123 @@
// and list via CollectionInfos, but an almost empty grid may be incorrectly identified
// as a list.
info.setClassName(GridView.class.getName());
+
+ if (mRecyclerView.mAdapter != null && mRecyclerView.mAdapter.getItemCount() > 1) {
+ info.addAction(AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION);
+ }
}
@Override
boolean performAccessibilityAction(int action, @Nullable Bundle args) {
- if (action == android.R.id.accessibilityActionScrollToPosition) {
+ // TODO (267511848): when U constants are finalized:
+ // - convert if/else blocks to switch statement
+ // - remove SDK check
+ // - remove the -1 check (this check makes accessibilityActionScrollInDirection
+ // no-op for < 34; see action definition in AccessibilityNodeInfoCompat.java).
+ if (action == AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION.getId()
+ && action != -1) {
+ final View viewWithAccessibilityFocus = findChildWithAccessibilityFocus();
+ if (viewWithAccessibilityFocus == null) {
+ // TODO(b/268487724#comment2): handle rare cases when the requesting service does
+ // not place accessibility focus on a child. Consider scrolling forward/backward?
+ return false;
+ }
+
+ // Direction must be specified.
+ if (args == null) {
+ return false;
+ }
+
+ final int direction = args.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_DIRECTION_INT, INVALID_POSITION);
+
+ if (!sSupportedDirectionsForActionScrollInDirection.contains(direction)) {
+ if (DEBUG) {
+ Log.w(TAG, "Direction equals " + direction
+ + "which is unsupported when using ACTION_SCROLL_IN_DIRECTION");
+ }
+ return false;
+ }
+
+ RecyclerView.ViewHolder vh =
+ mRecyclerView.getChildViewHolder(viewWithAccessibilityFocus);
+ if (vh == null) {
+ if (DEBUG) {
+ throw new RuntimeException(
+ "viewHolder is null for " + viewWithAccessibilityFocus);
+ }
+ return false;
+ }
+
+ int startingAdapterPosition = vh.getAbsoluteAdapterPosition();
+ int startingRow = getRowIndex(startingAdapterPosition);
+ int startingColumn = getColumnIndex(startingAdapterPosition);
+
+ if (startingRow < 0 || startingColumn < 0) {
+ if (DEBUG) {
+ throw new RuntimeException("startingRow equals " + startingRow + ", and "
+ + "startingColumn equals " + startingColumn + ", and neither can be "
+ + "less than 0.");
+ }
+ return false;
+ }
+
+ if (hasAccessibilityFocusChanged(startingAdapterPosition)) {
+ mRowWithAccessibilityFocus = startingRow;
+ mColumnWithAccessibilityFocus = startingColumn;
+ }
+
+ int scrollTargetPosition;
+
+ int row = (mRowWithAccessibilityFocus == INVALID_POSITION) ? startingRow
+ : mRowWithAccessibilityFocus;
+ int column = (mColumnWithAccessibilityFocus == INVALID_POSITION)
+ ? startingColumn : mColumnWithAccessibilityFocus;
+
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ scrollTargetPosition = findScrollTargetPositionOnTheLeft(row, column,
+ startingAdapterPosition);
+ break;
+ case View.FOCUS_RIGHT:
+ scrollTargetPosition =
+ findScrollTargetPositionOnTheRight(row, column,
+ startingAdapterPosition);
+ break;
+ case View.FOCUS_UP:
+ scrollTargetPosition = findScrollTargetPositionAbove(row, column,
+ startingAdapterPosition);
+ break;
+ case View.FOCUS_DOWN:
+ scrollTargetPosition = findScrollTargetPositionBelow(row, column,
+ startingAdapterPosition);
+ break;
+ default:
+ return false;
+ }
+
+ if (scrollTargetPosition == INVALID_POSITION
+ && mOrientation == RecyclerView.HORIZONTAL) {
+ // TODO (b/268487724): handle RTL.
+ // Handle case in grids with horizontal orientation where the scroll target is on
+ // a different row.
+ if (direction == View.FOCUS_LEFT) {
+ scrollTargetPosition = findPositionOfLastItemOnARowAboveForHorizontalGrid(
+ startingRow);
+ } else if (direction == View.FOCUS_RIGHT) {
+ scrollTargetPosition = findPositionOfFirstItemOnARowBelowForHorizontalGrid(
+ startingRow);
+ }
+ }
+
+ if (scrollTargetPosition != INVALID_POSITION) {
+ scrollToPosition(scrollTargetPosition);
+ mPositionTargetedByScrollInDirection = scrollTargetPosition;
+ return true;
+ }
+
+ return false;
+ } else if (action == android.R.id.accessibilityActionScrollToPosition) {
final int noRow = -1;
final int noColumn = -1;
if (args != null) {
@@ -228,6 +397,439 @@
return super.performAccessibilityAction(action, args);
}
+ private int findScrollTargetPositionOnTheRight(int startingRow, int startingColumn,
+ int startingAdapterPosition) {
+ int scrollTargetPosition = INVALID_POSITION;
+ for (int i = startingAdapterPosition + 1; i < getItemCount(); i++) {
+ int currentRow = getRowIndex(i);
+ int currentColumn = getColumnIndex(i);
+
+ if (currentRow < 0 || currentColumn < 0) {
+ if (DEBUG) {
+ throw new RuntimeException("currentRow equals " + currentRow + ", and "
+ + "currentColumn equals " + currentColumn + ", and neither can be "
+ + "less than 0.");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ /*
+ * For grids with vertical orientation...
+ * 1 2 3
+ * 4 5 5
+ * 6 7
+ * ... the scroll target may lie on the same or a following row.
+ */
+ // TODO (b/268487724): handle RTL.
+ if ((currentRow == startingRow && currentColumn > startingColumn)
+ || (currentRow > startingRow)) {
+ mRowWithAccessibilityFocus = currentRow;
+ mColumnWithAccessibilityFocus = currentColumn;
+ return i;
+ }
+ } else { // HORIZONTAL
+ /*
+ * For grids with horizontal orientation, the scroll target may span multiple
+ * rows. For example, in this grid...
+ * 1 4 6
+ * 2 5 7
+ * 3 5 8
+ * ... moving from 3 to 5 is considered staying on the "same row" because 5 spans
+ * multiple rows and the row indices for 5 include 3's row.
+ */
+ if (currentColumn > startingColumn && getRowIndices(i).contains(startingRow)) {
+ // Note: mRowWithAccessibilityFocus not updated since the scroll target is on
+ // the same row.
+ mColumnWithAccessibilityFocus = currentColumn;
+ return i;
+ }
+ }
+ }
+
+ return scrollTargetPosition;
+ }
+
+ private int findScrollTargetPositionOnTheLeft(int startingRow, int startingColumn,
+ int startingAdapterPosition) {
+ int scrollTargetPosition = INVALID_POSITION;
+ for (int i = startingAdapterPosition - 1; i >= 0; i--) {
+ int currentRow = getRowIndex(i);
+ int currentColumn = getColumnIndex(i);
+
+ if (currentRow < 0 || currentColumn < 0) {
+ if (DEBUG) {
+ throw new RuntimeException("currentRow equals " + currentRow + ", and "
+ + "currentColumn equals " + currentColumn + ", and neither can be "
+ + "less than 0.");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ /*
+ * For grids with vertical orientation...
+ * 1 2 3
+ * 4 5 5
+ * 6 7
+ * ... the scroll target may lie on the same or a preceding row.
+ */
+ // TODO (b/268487724): handle RTL.
+ if ((currentRow == startingRow && currentColumn < startingColumn)
+ || (currentRow < startingRow)) {
+ scrollTargetPosition = i;
+ mRowWithAccessibilityFocus = currentRow;
+ mColumnWithAccessibilityFocus = currentColumn;
+ break;
+ }
+ } else { // HORIZONTAL
+ /*
+ * For grids with horizontal orientation, the scroll target may span multiple
+ * rows. For example, in this grid...
+ * 1 4 6
+ * 2 5 7
+ * 3 5 8
+ * ... moving from 8 to 5 or from 7 to 5 is considered staying on the "same row"
+ * because the row indices for 5 include 8's and 7's row.
+ */
+ if (getRowIndices(i).contains(startingRow) && currentColumn < startingColumn) {
+ // Note: mRowWithAccessibilityFocus not updated since the scroll target is on
+ // the same row.
+ mColumnWithAccessibilityFocus = currentColumn;
+ return i;
+ }
+ }
+ }
+ return scrollTargetPosition;
+ }
+
+ private int findScrollTargetPositionAbove(int startingRow, int startingColumn,
+ int startingAdapterPosition) {
+ int scrollTargetPosition = INVALID_POSITION;
+ for (int i = startingAdapterPosition - 1; i >= 0; i--) {
+ int currentRow = getRowIndex(i);
+ int currentColumn = getColumnIndex(i);
+
+ if (currentRow < 0 || currentColumn < 0) {
+ if (DEBUG) {
+ throw new RuntimeException("currentRow equals " + currentRow + ", and "
+ + "currentColumn equals " + currentColumn + ", and neither can be "
+ + "less than 0.");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ /*
+ * The scroll target may span multiple columns. For example, in this grid...
+ * 1 2 3
+ * 4 4 5
+ * 6 7
+ * ... moving from 7 to 4 interprets as staying in second column, and moving from
+ * 6 to 4 interprets as staying in the first column.
+ */
+ if (currentRow < startingRow && getColumnIndices(i).contains(startingColumn)) {
+ scrollTargetPosition = i;
+ mRowWithAccessibilityFocus = currentRow;
+ // Note: mColumnWithAccessibilityFocus not updated since the scroll target is on
+ // the same column.
+ break;
+ }
+ } else { // HORIZONTAL
+ /*
+ * The scroll target may span multiple rows. In this grid...
+ * 1 4
+ * 2 5
+ * 2
+ * 3
+ * ... 2 spans two rows and moving up from 3 to 2 interprets moving to the third
+ * row.
+ */
+ if (currentRow < startingRow && currentColumn == startingColumn) {
+ Set<Integer> rowIndices = getRowIndices(i);
+ scrollTargetPosition = i;
+ mRowWithAccessibilityFocus = Collections.max(rowIndices);
+ // Note: mColumnWithAccessibilityFocus not updated since the scroll target is on
+ // the same column.
+ break;
+ }
+ }
+ }
+ return scrollTargetPosition;
+ }
+
+ private int findScrollTargetPositionBelow(int startingRow, int startingColumn,
+ int startingAdapterPosition) {
+ int scrollTargetPosition = INVALID_POSITION;
+ for (int i = startingAdapterPosition + 1; i < getItemCount(); i++) {
+ int currentRow = getRowIndex(i);
+ int currentColumn = getColumnIndex(i);
+
+ if (currentRow < 0 || currentColumn < 0) {
+ if (DEBUG) {
+ throw new RuntimeException("currentRow equals " + currentRow + ", and "
+ + "currentColumn equals " + currentColumn + ", and neither can be "
+ + "less than 0.");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ /*
+ * The scroll target may span multiple columns. For example, in this grid...
+ * 1 2 3
+ * 4 4 5
+ * 6 7
+ * ... moving from 2 to 4 interprets as staying in second column, and moving from
+ * 1 to 4 interprets as staying in the first column.
+ */
+ if ((currentRow > startingRow) && (currentColumn == startingColumn
+ || getColumnIndices(i).contains(startingColumn))) {
+ scrollTargetPosition = i;
+ mRowWithAccessibilityFocus = currentRow;
+ break;
+ }
+ } else { // HORIZONTAL
+ /*
+ * The scroll target may span multiple rows. In this grid...
+ * 1 4
+ * 2 5
+ * 2
+ * 3
+ * ... 2 spans two rows and moving down from 1 to 2 interprets moving to the second
+ * row.
+ */
+ if (currentRow > startingRow && currentColumn == startingColumn) {
+ scrollTargetPosition = i;
+ mRowWithAccessibilityFocus = getRowIndex(i);
+ break;
+ }
+ }
+ }
+ return scrollTargetPosition;
+ }
+
+ @SuppressWarnings("ConstantConditions") // For the spurious NPE warning related to getting a
+ // value from a map using one of the map keys.
+ int findPositionOfLastItemOnARowAboveForHorizontalGrid(int startingRow) {
+ if (startingRow < 0) {
+ if (DEBUG) {
+ throw new RuntimeException(
+ "startingRow equals " + startingRow + ". It cannot be less than zero");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ // This only handles cases of grids with horizontal orientation.
+ if (DEBUG) {
+ Log.w(TAG, "You should not "
+ + "use findPositionOfLastItemOnARowAboveForHorizontalGrid(...) with grids "
+ + "with VERTICAL orientation");
+ }
+ return INVALID_POSITION;
+ }
+
+ // Map where the keys are row numbers and values are the adapter positions of the last
+ // item in each row. This map is used to locate a scroll target on a previous row in grids
+ // with horizontal orientation. In this example...
+ // 1 4 7
+ // 2 5 8
+ // 3 6
+ // ... the generated map - {2 -> 5, 1 -> 7, 0 -> 6} - can be used to scroll from,
+ // say, "2" (adapter position 1) in the second row to "7" (adapter position 6) in the
+ // preceding row.
+ //
+ // Sometimes cells span multiple rows. In this example:
+ // 1 4 7
+ // 2 5 7
+ // 3 6 8
+ // ... the generated map - {0 -> 6, 1 -> 6, 2 -> 7} - can be used to scroll left from,
+ // say, "3" (adapter position 2) in the third row to "7" (adapter position 6) on the
+ // second row, and then to "5" (adapter position 4).
+ Map<Integer, Integer> rowToLastItemPositionMap = new TreeMap<>(Collections.reverseOrder());
+ for (int position = 0; position < getItemCount(); position++) {
+ Set<Integer> rows = getRowIndices(position);
+ for (int row: rows) {
+ if (row < 0) {
+ if (DEBUG) {
+ throw new RuntimeException(
+ "row equals " + row + ". It cannot be less than zero");
+ }
+ return INVALID_POSITION;
+ }
+ rowToLastItemPositionMap.put(row, position);
+ }
+ }
+
+ for (int row : rowToLastItemPositionMap.keySet()) {
+ if (row < startingRow) {
+ int scrollTargetPosition = rowToLastItemPositionMap.get(row);
+ mRowWithAccessibilityFocus = row;
+ mColumnWithAccessibilityFocus = getColumnIndex(scrollTargetPosition);
+ return scrollTargetPosition;
+ }
+ }
+ return INVALID_POSITION;
+ }
+
+ @SuppressWarnings("ConstantConditions") // For the spurious NPE warning related to getting a
+ // value from a map using one of the map keys.
+ int findPositionOfFirstItemOnARowBelowForHorizontalGrid(int startingRow) {
+ if (startingRow < 0) {
+ if (DEBUG) {
+ throw new RuntimeException(
+ "startingRow equals " + startingRow + ". It cannot be less than zero");
+ }
+ return INVALID_POSITION;
+ }
+
+ if (mOrientation == VERTICAL) {
+ // This only handles cases of grids with horizontal orientation.
+ if (DEBUG) {
+ Log.w(TAG, "You should not "
+ + "use findPositionOfFirstItemOnARowBelowForHorizontalGrid(...) with grids "
+ + "with VERTICAL orientation");
+ }
+ return INVALID_POSITION;
+ }
+
+ // Map where the keys are row numbers and values are the adapter positions of the first
+ // item in each row. This map is used to locate a scroll target on a following row in grids
+ // with horizontal orientation. In this example:
+ // 1 4 7
+ // 2 5 8
+ // 3 6
+ // ... the generated map - {0 -> 0, 1 -> 1, 2 -> 2} - can be used to scroll from, say,
+ // "7" (adapter position 6) in the first row to "2" (adapter position 1) in the next row.
+ // Sometimes cells span multiple rows. In this example:
+ // 1 3 6
+ // 1 4 7
+ // 2 5 8
+ // ... the generated map - {0 -> 0, 1 -> 0, 2 -> 1} - can be used to scroll right from,
+ // say, "6" (adapter position 5) in the first row to "1" (adapter position 0) on the
+ // second row, and then to "4" (adapter position 3).
+ Map<Integer, Integer> rowToFirstItemPositionMap = new TreeMap<>();
+ for (int position = 0; position < getItemCount(); position++) {
+ Set<Integer> rows = getRowIndices(position);
+ for (int row : rows) {
+ if (row < 0) {
+ if (DEBUG) {
+ throw new RuntimeException(
+ "row equals " + row + ". It cannot be less than zero");
+ }
+ return INVALID_POSITION;
+ }
+ // We only care about the first item on each row.
+ if (!rowToFirstItemPositionMap.containsKey(row)) {
+ rowToFirstItemPositionMap.put(row, position);
+ }
+ }
+ }
+
+ for (int row : rowToFirstItemPositionMap.keySet()) {
+ if (row > startingRow) {
+ int scrollTargetPosition = rowToFirstItemPositionMap.get(row);
+ mRowWithAccessibilityFocus = row;
+ mColumnWithAccessibilityFocus = 0;
+ return scrollTargetPosition;
+ }
+ }
+ return INVALID_POSITION;
+ }
+
+ /**
+ * Returns the row index associated with a position. If the item at this position spans multiple
+ * rows, it returns the first row index. To get all row indices for a position, use
+ * {@link #getRowIndices(int)}.
+ */
+ private int getRowIndex(int position) {
+ return mOrientation == VERTICAL ? getSpanGroupIndex(mRecyclerView.mRecycler,
+ mRecyclerView.mState, position) : getSpanIndex(mRecyclerView.mRecycler,
+ mRecyclerView.mState, position);
+ }
+
+ /**
+ * Returns the column index associated with a position. If the item at this position spans
+ * multiple columns, it returns the first column index. To get all column indices, use
+ * {@link #getColumnIndices(int)}.
+ */
+ private int getColumnIndex(int position) {
+ return mOrientation == HORIZONTAL ? getSpanGroupIndex(mRecyclerView.mRecycler,
+ mRecyclerView.mState, position) : getSpanIndex(mRecyclerView.mRecycler,
+ mRecyclerView.mState, position);
+ }
+
+ /**
+ * Returns the row indices for a cell associated with {@code position}. For example, in this
+ * grid...
+ * 0 2 3
+ * 1 2 4
+ * ... the rows for the view at position 2 will be [0, 1] and the rows for position 3 will be
+ * [0].
+ */
+ private Set<Integer> getRowIndices(int position) {
+ return getRowOrColumnIndices(getRowIndex(position), position);
+ }
+
+ /**
+ * Returns the column indices for a cell associated with {@code position}. For example, in this
+ * grid...
+ * 0 1
+ * 2 2
+ * 3 4
+ * ... the columns for the view at position 2 will be [0, 1] and the columns for position 3
+ * will be [0].
+ */
+ private Set<Integer> getColumnIndices(int position) {
+ return getRowOrColumnIndices(getColumnIndex(position), position);
+ }
+
+ private Set<Integer> getRowOrColumnIndices(int rowOrColumnIndex, int position) {
+ Set<Integer> indices = new HashSet<>();
+ int spanSize = getSpanSize(mRecyclerView.mRecycler, mRecyclerView.mState, position);
+ for (int i = rowOrColumnIndex; i < rowOrColumnIndex + spanSize; i++) {
+ indices.add(i);
+ }
+ return indices;
+ }
+
+ @Nullable
+ private View findChildWithAccessibilityFocus() {
+ View child = null;
+ // SDK check needed for View#isAccessibilityFocused()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ boolean childFound = false;
+ int i;
+ for (i = 0; i < getChildCount(); i++) {
+ if (Api21Impl.isAccessibilityFocused(Objects.requireNonNull(getChildAt(i)))) {
+ childFound = true;
+ break;
+ }
+ }
+ if (childFound) {
+ child = getChildAt(i);
+ }
+ }
+ return child;
+ }
+
+ /**
+ * Returns true if the values stored in {@link #mRowWithAccessibilityFocus} and
+ * {@link #mColumnWithAccessibilityFocus} are not correct for the view at
+ * {@code adapterPosition}.
+ *
+ * Note that for cells that span multiple rows or multiple columns, {@link
+ * #mRowWithAccessibilityFocus} and {@link #mColumnWithAccessibilityFocus} can be set to more
+ * than one of several values. Accessibility focus is considered unchanged if any of the
+ * possible row values for a cell are the same as {@link #mRowWithAccessibilityFocus} and any
+ * of the possible column values are the same as {@link #mColumnWithAccessibilityFocus}.
+ */
+ private boolean hasAccessibilityFocusChanged(int adapterPosition) {
+ return !getRowIndices(adapterPosition).contains(mRowWithAccessibilityFocus)
+ || !getColumnIndices(adapterPosition).contains(mColumnWithAccessibilityFocus);
+ }
+
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) {
@@ -244,6 +846,19 @@
public void onLayoutCompleted(RecyclerView.State state) {
super.onLayoutCompleted(state);
mPendingSpanCountChange = false;
+ if (mPositionTargetedByScrollInDirection != INVALID_POSITION) {
+ View viewTargetedByScrollInDirection = findViewByPosition(
+ mPositionTargetedByScrollInDirection);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && viewTargetedByScrollInDirection != null) {
+ // Send event after the scroll associated with ACTION_SCROLL_IN_DIRECTION (see
+ // performAccessibilityAction()) concludes and layout completes. Accessibility
+ // services can listen for this event and change UI state as needed.
+ viewTargetedByScrollInDirection.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL);
+ mPositionTargetedByScrollInDirection = INVALID_POSITION;
+ }
+ }
}
private void clearPreLayoutSpanMappingCache() {
@@ -1506,4 +2121,17 @@
return mSpanSize;
}
}
+
+
+ @RequiresApi(21)
+ private static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static boolean isAccessibilityFocused(@NonNull View view) {
+ return view.isAccessibilityFocused();
+ }
+ }
}
\ No newline at end of file
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index db5526b..7df72f0 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -11230,10 +11230,12 @@
if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
info.setScrollable(true);
+ info.setGranularScrollingSupported(true);
}
if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
info.setScrollable(true);
+ info.setGranularScrollingSupported(true);
}
final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
AccessibilityNodeInfoCompat.CollectionInfoCompat
@@ -11428,6 +11430,7 @@
height = rect.height();
width = rect.width();
}
+
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
if (mRecyclerView.canScrollVertically(-1)) {
@@ -11446,9 +11449,54 @@
}
break;
}
+
if (vScroll == 0 && hScroll == 0) {
return false;
}
+
+ float granularScrollAmount = 1F; // The default value.
+
+ if (args != null) {
+ granularScrollAmount = args.getFloat(
+ AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT, 1F);
+ if (granularScrollAmount < 0) {
+ if (sDebugAssertionsEnabled) {
+ throw new IllegalArgumentException(
+ "attempting to use ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT with a "
+ + "negative value (" + granularScrollAmount + ")");
+ }
+ return false;
+ }
+ }
+
+ if (Float.compare(granularScrollAmount, Float.POSITIVE_INFINITY) == 0) {
+ // Assume that the client wants to scroll as far as possible. For
+ // ACTION_SCROLL_BACKWARD, this means scrolling to the beginning of the collection.
+ // For ACTION_SCROLL_FORWARD, this means scrolling to the end of the collection.
+
+ if (mRecyclerView.mAdapter == null) {
+ return false;
+ }
+ switch (action) {
+ case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
+ mRecyclerView.smoothScrollToPosition(0);
+ break;
+ case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
+ mRecyclerView.smoothScrollToPosition(
+ mRecyclerView.mAdapter.getItemCount() - 1);
+ break;
+ }
+ return true;
+ }
+
+ // No adjustments needed to scroll values if granular scroll amount is 1F, which is
+ // the default, or 0F, which is undefined.
+ if (Float.compare(1F, granularScrollAmount) != 0 && Float.compare(0F,
+ granularScrollAmount) != 0) {
+ hScroll = (int) (hScroll * granularScrollAmount);
+ vScroll = (int) (vScroll * granularScrollAmount);
+ }
+
mRecyclerView.smoothScrollBy(hScroll, vScroll, null, UNDEFINED_DURATION, true);
return true;
}
diff --git a/room/room-rxjava2/lint-baseline.xml b/room/room-rxjava2/lint-baseline.xml
index 02e92c5..656b9b3 100644
--- a/room/room-rxjava2/lint-baseline.xml
+++ b/room/room-rxjava2/lint-baseline.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
-
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
diff --git a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
index 25e7453..bb037aa 100644
--- a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
+++ b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
@@ -41,6 +41,7 @@
android:label="@string/main_activity_label">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+ <action android:name="android.media.action.TRANSFER_MEDIA"/>
<category android:name="com.example.androidx.SAMPLE_CODE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -68,6 +69,17 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".activities.RouteListingPreferenceActivity"
+ android:configChanges="orientation|screenSize"
+ android:exported="false"
+ android:label="Route Listing Preference">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.example.androidx.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".activities.systemrouting.SystemRoutingActivity"
android:exported="false" />
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RoutesManager.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RoutesManager.java
index 7e69d77..f4d1b6f 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RoutesManager.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RoutesManager.java
@@ -23,17 +23,23 @@
import static com.example.androidx.mediarouting.data.RouteItem.PlaybackType.REMOTE;
import static com.example.androidx.mediarouting.data.RouteItem.VolumeHandling.VARIABLE;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.core.os.BuildCompat;
import androidx.mediarouter.media.MediaRouter;
import androidx.mediarouter.media.MediaRouterParams;
+import androidx.mediarouter.media.RouteListingPreference;
+import com.example.androidx.mediarouting.activities.MainActivity;
import com.example.androidx.mediarouting.data.RouteItem;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -42,6 +48,7 @@
public final class RoutesManager {
private static final String VARIABLE_VOLUME_BASIC_ROUTE_ID = "variable_basic";
+ private static final String SENDER_DRIVEN_BASIC_ROUTE_ID = "sender_driven_route";
private static final int VOLUME_MAX = 25;
private static final int VOLUME_DEFAULT = 5;
@@ -51,12 +58,18 @@
private final Map<String, RouteItem> mRouteItems;
private boolean mDynamicRoutingEnabled;
private DialogType mDialogType;
+ private final MediaRouter mMediaRouter;
+ private boolean mRouteListingPreferenceEnabled;
+ private boolean mRouteListingSystemOrderingPreferred;
+ private List<RouteListingPreferenceItemHolder> mRouteListingPreferenceItems;
private RoutesManager(Context context) {
mContext = context;
mDynamicRoutingEnabled = true;
mDialogType = DialogType.OUTPUT_SWITCHER;
mRouteItems = new HashMap<>();
+ mRouteListingPreferenceItems = Collections.emptyList();
+ mMediaRouter = MediaRouter.getInstance(context);
initTestRoutes();
}
@@ -113,6 +126,76 @@
mRouteItems.put(routeItem.getId(), routeItem);
}
+ /**
+ * Returns whether route listing preference is enabled.
+ *
+ * @see #setRouteListingPreferenceEnabled
+ */
+ public boolean isRouteListingPreferenceEnabled() {
+ return mRouteListingPreferenceEnabled;
+ }
+
+ /**
+ * Sets whether the use of route listing preference is enabled or not.
+ *
+ * <p>If route listing preference is enabled, the route listing preference configuration for
+ * this app is maintained following the item list provided via {@link
+ * #setRouteListingPreferenceItems}. Otherwise, if route listing preference is disabled, the
+ * route listing preference for this app is set to null.
+ *
+ * <p>Does not affect the system's state if called on a device running API 33 or older.
+ */
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public void setRouteListingPreferenceEnabled(boolean routeListingPreferenceEnabled) {
+ mRouteListingPreferenceEnabled = routeListingPreferenceEnabled;
+ onRouteListingPreferenceChanged();
+ }
+
+ /** Returns whether the system ordering for route listing is preferred. */
+ public boolean getRouteListingSystemOrderingPreferred() {
+ return mRouteListingSystemOrderingPreferred;
+ }
+
+ /**
+ * Sets whether to prefer the system ordering for route listing.
+ *
+ * <p>True means that the ordering for route listing is the one in the {@link #getRouteItems()}
+ * list. If false, the ordering of said list is ignored, and the system uses its builtin
+ * ordering for the items.
+ *
+ * <p>Does not affect the system's state if called on a device running API 33 or older.
+ */
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public void setRouteListingSystemOrderingPreferred(
+ boolean routeListingSystemOrderringPreferred) {
+ mRouteListingSystemOrderingPreferred = routeListingSystemOrderringPreferred;
+ onRouteListingPreferenceChanged();
+ }
+
+ /**
+ * The current list of route listing preference items, as set via {@link
+ * #setRouteListingPreferenceItems}.
+ */
+ @NonNull
+ public List<RouteListingPreferenceItemHolder> getRouteListingPreferenceItems() {
+ return mRouteListingPreferenceItems;
+ }
+
+ /**
+ * Sets the route listing preference items.
+ *
+ * <p>Does not affect the system's state if called on a device running API 33 or older.
+ *
+ * @see #setRouteListingPreferenceEnabled
+ */
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ public void setRouteListingPreferenceItems(
+ @NonNull List<RouteListingPreferenceItemHolder> preference) {
+ mRouteListingPreferenceItems =
+ Collections.unmodifiableList(new ArrayList<>(preference));
+ onRouteListingPreferenceChanged();
+ }
+
/** Changes the media router dialog type with the type stored in {@link RoutesManager} */
public void reloadDialogType() {
MediaRouter mediaRouter = MediaRouter.getInstance(mContext.getApplicationContext());
@@ -191,10 +274,58 @@
r4.setVolume(VOLUME_DEFAULT);
r4.setCanDisconnect(true);
+ RouteItem r5 = new RouteItem();
+ r5.setId(SENDER_DRIVEN_BASIC_ROUTE_ID + "1");
+ r5.setName(r.getString(R.string.sender_driven_route_name1));
+ r5.setDescription(r.getString(R.string.sample_route_description));
+ r5.setControlFilter(BASIC);
+ r5.setDeviceType(TV);
+ r5.setPlaybackStream(MUSIC);
+ r5.setPlaybackType(REMOTE);
+ r5.setVolumeHandling(VARIABLE);
+ r5.setVolumeMax(VOLUME_MAX);
+ r5.setVolume(VOLUME_DEFAULT);
+ r5.setCanDisconnect(true);
+ r5.setSenderDriven(true);
+
+ RouteItem r6 = new RouteItem();
+ r6.setId(SENDER_DRIVEN_BASIC_ROUTE_ID + "2");
+ r6.setName(r.getString(R.string.sender_driven_route_name2));
+ r6.setDescription(r.getString(R.string.sample_route_description));
+ r6.setControlFilter(BASIC);
+ r6.setDeviceType(TV);
+ r6.setPlaybackStream(MUSIC);
+ r6.setPlaybackType(REMOTE);
+ r6.setVolumeHandling(VARIABLE);
+ r6.setVolumeMax(VOLUME_MAX);
+ r6.setVolume(VOLUME_DEFAULT);
+ r6.setCanDisconnect(true);
+ r6.setSenderDriven(true);
+
mRouteItems.put(r1.getId(), r1);
mRouteItems.put(r2.getId(), r2);
mRouteItems.put(r3.getId(), r3);
mRouteItems.put(r4.getId(), r4);
+ mRouteItems.put(r5.getId(), r5);
+ mRouteItems.put(r6.getId(), r6);
+ }
+
+ private void onRouteListingPreferenceChanged() {
+ RouteListingPreference routeListingPreference = null;
+ if (mRouteListingPreferenceEnabled) {
+ ArrayList<RouteListingPreference.Item> items = new ArrayList<>();
+ for (RouteListingPreferenceItemHolder item : mRouteListingPreferenceItems) {
+ items.add(item.mItem);
+ }
+ routeListingPreference =
+ new RouteListingPreference.Builder()
+ .setItems(items)
+ .setLinkedItemComponentName(
+ new ComponentName(mContext, MainActivity.class))
+ .setUseSystemOrdering(mRouteListingSystemOrderingPreferred)
+ .build();
+ }
+ mMediaRouter.setRouteListingPreference(routeListingPreference);
}
public enum DialogType {
@@ -202,4 +333,38 @@
DYNAMIC_GROUP,
OUTPUT_SWITCHER
}
+
+ /**
+ * Holds a {@link RouteListingPreference.Item} and the associated route's name.
+ *
+ * <p>Convenient pair-like class for populating UI elements, ensuring we have an associated
+ * route name for each route listing preference item even after the corresponding route no
+ * longer exists.
+ */
+ public static final class RouteListingPreferenceItemHolder {
+
+ @NonNull public final RouteListingPreference.Item mItem;
+ @NonNull public final String mRouteName;
+
+ public RouteListingPreferenceItemHolder(
+ @NonNull RouteListingPreference.Item item, @NonNull String routeName) {
+ mItem = item;
+ mRouteName = routeName;
+ }
+
+ /** Returns the name of the corresponding route. */
+ @Override
+ @NonNull
+ public String toString() {
+ return mRouteName;
+ }
+
+ /**
+ * Returns whether the contained {@link RouteListingPreference.Item} has the given {@code
+ * flag} set.
+ */
+ public boolean hasFlag(int flag) {
+ return (mItem.getFlags() & flag) == flag;
+ }
+ }
}
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
index 7741f9f..aed9f13 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
@@ -16,6 +16,8 @@
package com.example.androidx.mediarouting.activities;
+import static com.example.androidx.mediarouting.ui.UiUtils.setUpEnumBasedSpinner;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -24,17 +26,14 @@
import android.os.IBinder;
import android.text.Editable;
import android.text.TextWatcher;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
-import android.widget.Spinner;
import android.widget.Switch;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.util.Consumer;
import com.example.androidx.mediarouting.R;
import com.example.androidx.mediarouting.RoutesManager;
@@ -49,7 +48,6 @@
private ServiceConnection mConnection;
private RoutesManager mRoutesManager;
private RouteItem mRouteItem;
- private Switch mCanDisconnectSwitch;
/** Launches the activity. */
public static void launchActivity(@NonNull Context context, @Nullable String routeId) {
@@ -75,8 +73,6 @@
mRouteItem = RouteItem.copyOf(mRouteItem);
}
- mCanDisconnectSwitch = findViewById(R.id.cam_disconnect_switch);
-
setUpViews();
}
@@ -149,19 +145,19 @@
String.valueOf(mRouteItem.getVolumeMax()),
mewVolumeMax -> mRouteItem.setVolumeMax(Integer.parseInt(mewVolumeMax)));
- setUpCanDisconnectSwitch();
+ setUpSwitch(
+ findViewById(R.id.can_disconnect_switch),
+ mRouteItem.isCanDisconnect(),
+ newValue -> mRouteItem.setCanDisconnect(newValue));
+
+ setUpSwitch(
+ findViewById(R.id.is_sender_driven_switch),
+ mRouteItem.isSenderDriven(),
+ newValue -> mRouteItem.setSenderDriven(newValue));
setUpSaveButton();
}
- private void setUpCanDisconnectSwitch() {
- mCanDisconnectSwitch.setChecked(mRouteItem.isCanDisconnect());
- mCanDisconnectSwitch.setOnCheckedChangeListener(
- (compoundButton, b) -> {
- mRouteItem.setCanDisconnect(b);
- });
- }
-
private void setUpSaveButton() {
Button saveButton = findViewById(R.id.save_button);
saveButton.setOnClickListener(
@@ -172,10 +168,14 @@
});
}
+ private static void setUpSwitch(Switch switchWidget, boolean currentValue,
+ Consumer<Boolean> propertySetter) {
+ switchWidget.setChecked(currentValue);
+ switchWidget.setOnCheckedChangeListener((compoundButton, b) -> propertySetter.accept(b));
+ }
+
private static void setUpEditText(
- EditText editText,
- String currentValue,
- RoutePropertySetter<String> routePropertySetter) {
+ EditText editText, String currentValue, Consumer<String> propertySetter) {
editText.setText(currentValue);
editText.addTextChangedListener(
new TextWatcher() {
@@ -185,7 +185,7 @@
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
- routePropertySetter.accept(charSequence.toString());
+ propertySetter.accept(charSequence.toString());
}
@Override
@@ -193,36 +193,6 @@
});
}
- private static void setUpEnumBasedSpinner(
- Context context,
- Spinner spinner,
- Enum<?> anEnum,
- RoutePropertySetter<Enum<?>> routePropertySetter) {
- Enum<?>[] enumValues = anEnum.getDeclaringClass().getEnumConstants();
- ArrayAdapter<Enum<?>> adapter =
- new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, enumValues);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(adapter);
- spinner.setSelection(anEnum.ordinal());
-
- spinner.setOnItemSelectedListener(
- new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(
- AdapterView<?> adapterView, View view, int i, long l) {
- routePropertySetter.accept(
- anEnum.getDeclaringClass().getEnumConstants()[i]);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> adapterView) {}
- });
- }
-
- private interface RoutePropertySetter<T> {
- void accept(T value);
- }
-
private class ProviderServiceConnection implements ServiceConnection {
@Override
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
index 3558f3a..9247677 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
@@ -45,6 +45,7 @@
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
@@ -66,6 +67,7 @@
import androidx.mediarouter.media.MediaRouter.ProviderInfo;
import androidx.mediarouter.media.MediaRouter.RouteInfo;
import androidx.mediarouter.media.MediaRouterParams;
+import androidx.mediarouter.media.RouteListingPreference;
import com.example.androidx.mediarouting.MyMediaRouteControllerDialog;
import com.example.androidx.mediarouting.R;
@@ -82,6 +84,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
+import java.util.List;
/**
* Demonstrates how to use the {@link MediaRouter} API to build an application that allows the user
@@ -269,6 +272,10 @@
mSessionManager.setCallback(new SampleSessionManagerCallback());
updateUi();
+
+ if (RouteListingPreference.ACTION_TRANSFER_MEDIA.equals(getIntent().getAction())) {
+ showMediaTransferToast();
+ }
}
@Override
@@ -333,6 +340,26 @@
requestPostNotificationsPermission();
}
+ private void showMediaTransferToast() {
+ String routeId = getIntent().getStringExtra(RouteListingPreference.EXTRA_ROUTE_ID);
+ List<RouteInfo> routes = mMediaRouter.getRoutes();
+ String requestedRouteName = null;
+ for (RouteInfo route : routes) {
+ if (route.getId().equals(routeId)) {
+ requestedRouteName = route.getName();
+ break;
+ }
+ }
+ String stringToDisplay =
+ requestedRouteName != null
+ ? "Transfer requested to " + requestedRouteName
+ : "Transfer requested to unknown route: " + routeId;
+
+ // TODO(b/266561322): Replace the toast with a Dialog that allows the user to either
+ // transfer playback to the requested route, or dismiss the intent.
+ Toast.makeText(/* context= */ this, stringToDisplay, Toast.LENGTH_LONG).show();
+ }
+
private void requestDisplayOverOtherAppsPermission() {
// Need overlay permission for emulating remote display.
if (Build.VERSION.SDK_INT >= 23 && !Api23Impl.canDrawOverlays(this)) {
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java
new file mode 100644
index 0000000..308d3c8
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.androidx.mediarouting.activities;
+
+import static androidx.mediarouter.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION;
+import static androidx.mediarouter.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION_MANAGED;
+import static androidx.mediarouter.media.RouteListingPreference.Item.FLAG_SUGGESTED;
+import static androidx.mediarouter.media.RouteListingPreference.Item.SUBTEXT_CUSTOM;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.os.BuildCompat;
+import androidx.mediarouter.media.MediaRouter;
+import androidx.mediarouter.media.RouteListingPreference;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.androidx.mediarouting.R;
+import com.example.androidx.mediarouting.RoutesManager;
+import com.example.androidx.mediarouting.RoutesManager.RouteListingPreferenceItemHolder;
+import com.example.androidx.mediarouting.ui.UiUtils;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Allows the user to manage the route listing preference of this app. */
+public class RouteListingPreferenceActivity extends AppCompatActivity {
+
+ private RoutesManager mRoutesManager;
+ private RecyclerView mRouteListingPreferenceRecyclerView;
+
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!BuildCompat.isAtLeastU()) {
+ Toast.makeText(
+ /* context= */ this,
+ "Route Listing Preference requires Android U+",
+ Toast.LENGTH_LONG)
+ .show();
+ finish();
+ return;
+ }
+
+ setContentView(R.layout.activity_route_listing_preference);
+
+ mRoutesManager = RoutesManager.getInstance(/* context= */ this);
+
+ Switch preferSystemOrderingSwitch = findViewById(R.id.prefer_system_ordering_switch);
+ preferSystemOrderingSwitch.setChecked(
+ mRoutesManager.getRouteListingSystemOrderingPreferred());
+ preferSystemOrderingSwitch.setOnCheckedChangeListener(
+ (unusedButton, isChecked) -> {
+ mRoutesManager.setRouteListingSystemOrderingPreferred(isChecked);
+ });
+ preferSystemOrderingSwitch.setEnabled(mRoutesManager.isRouteListingPreferenceEnabled());
+
+ Switch enableRouteListingPreferenceSwitch =
+ findViewById(R.id.enable_route_listing_preference_switch);
+ enableRouteListingPreferenceSwitch.setChecked(
+ mRoutesManager.isRouteListingPreferenceEnabled());
+ enableRouteListingPreferenceSwitch.setOnCheckedChangeListener(
+ (unusedButton, isChecked) -> {
+ mRoutesManager.setRouteListingPreferenceEnabled(isChecked);
+ preferSystemOrderingSwitch.setEnabled(isChecked);
+ });
+
+ mRouteListingPreferenceRecyclerView =
+ findViewById(R.id.route_listing_preference_recycler_view);
+ new ItemTouchHelper(new RecyclerViewCallback())
+ .attachToRecyclerView(mRouteListingPreferenceRecyclerView);
+ mRouteListingPreferenceRecyclerView.setLayoutManager(
+ new LinearLayoutManager(/* context= */ this));
+ mRouteListingPreferenceRecyclerView.setHasFixedSize(true);
+ mRouteListingPreferenceRecyclerView.setAdapter(
+ new RouteListingPreferenceRecyclerViewAdapter());
+
+ FloatingActionButton newRouteButton =
+ findViewById(R.id.new_route_listing_preference_item_button);
+ newRouteButton.setOnClickListener(
+ view ->
+ setUpRouteListingPreferenceItemEditionDialog(
+ mRoutesManager.getRouteListingPreferenceItems().size()));
+ }
+
+ private void setUpRouteListingPreferenceItemEditionDialog(int itemPositionInList) {
+ List<RouteListingPreferenceItemHolder> routeListingPreference =
+ mRoutesManager.getRouteListingPreferenceItems();
+ List<MediaRouter.RouteInfo> routesWithNoAssociatedListingPreferenceItem =
+ getRoutesWithNoAssociatedListingPreferenceItem();
+ if (itemPositionInList == routeListingPreference.size()
+ && routesWithNoAssociatedListingPreferenceItem.isEmpty()) {
+ Toast.makeText(/* context= */ this, "No (more) routes available", Toast.LENGTH_LONG)
+ .show();
+ return;
+ }
+ View dialogView =
+ getLayoutInflater()
+ .inflate(R.layout.route_listing_preference_item_dialog, /* root= */ null);
+
+ Spinner routeSpinner = dialogView.findViewById(R.id.rlp_item_dialog_route_name_spinner);
+ List<RouteListingPreferenceItemHolder> spinnerEntries = new ArrayList<>();
+
+ Spinner selectionBehaviorSpinner =
+ dialogView.findViewById(R.id.rlp_item_dialog_selection_behavior_spinner);
+ UiUtils.setUpEnumBasedSpinner(
+ /* context= */ this,
+ selectionBehaviorSpinner,
+ RouteListingPreferenceItemSelectionBehavior.SELECTION_BEHAVIOR_TRANSFER,
+ (unused) -> {});
+
+ CheckBox ongoingSessionCheckBox =
+ dialogView.findViewById(R.id.rlp_item_dialog_ongoing_session_checkbox);
+ CheckBox sessionManagedCheckBox =
+ dialogView.findViewById(R.id.rlp_item_dialog_session_managed_checkbox);
+ CheckBox suggestedRouteCheckBox =
+ dialogView.findViewById(R.id.rlp_item_dialog_suggested_checkbox);
+
+ Spinner subtextSpinner = dialogView.findViewById(R.id.rlp_item_dialog_subtext_spinner);
+ UiUtils.setUpEnumBasedSpinner(
+ /* context= */ this,
+ subtextSpinner,
+ RouteListingPreferenceItemSubtext.SUBTEXT_NONE,
+ (unused) -> {});
+
+ if (itemPositionInList < routeListingPreference.size()) {
+ RouteListingPreferenceItemHolder itemHolder =
+ routeListingPreference.get(itemPositionInList);
+ spinnerEntries.add(itemHolder);
+ int selectionBehaviorOrdinalIndex =
+ RouteListingPreferenceItemSelectionBehavior.fromConstant(
+ itemHolder.mItem.getSelectionBehavior())
+ .ordinal();
+ selectionBehaviorSpinner.setSelection(selectionBehaviorOrdinalIndex);
+ ongoingSessionCheckBox.setChecked(itemHolder.hasFlag(FLAG_ONGOING_SESSION));
+ sessionManagedCheckBox.setChecked(itemHolder.hasFlag(FLAG_ONGOING_SESSION_MANAGED));
+ suggestedRouteCheckBox.setChecked(itemHolder.hasFlag(FLAG_SUGGESTED));
+ int subtextOrdinalIndex =
+ RouteListingPreferenceItemSubtext.fromConstant(itemHolder.mItem.getSubText())
+ .ordinal();
+ subtextSpinner.setSelection(subtextOrdinalIndex);
+ }
+ for (MediaRouter.RouteInfo routeInfo : routesWithNoAssociatedListingPreferenceItem) {
+ spinnerEntries.add(
+ new RouteListingPreferenceItemHolder(
+ new RouteListingPreference.Item.Builder(routeInfo.getId()).build(),
+ routeInfo.getName()));
+ }
+ routeSpinner.setAdapter(
+ new ArrayAdapter<>(
+ /* context= */ this, android.R.layout.simple_spinner_item, spinnerEntries));
+
+ AlertDialog editRlpItemDialog =
+ new AlertDialog.Builder(this)
+ .setView(dialogView)
+ .setPositiveButton(
+ "Accept",
+ (unusedDialog, unusedWhich) -> {
+ RouteListingPreferenceItemHolder item =
+ (RouteListingPreferenceItemHolder)
+ routeSpinner.getSelectedItem();
+ RouteListingPreferenceItemSelectionBehavior selectionBehavior =
+ (RouteListingPreferenceItemSelectionBehavior)
+ selectionBehaviorSpinner.getSelectedItem();
+ int flags = 0;
+ flags |=
+ ongoingSessionCheckBox.isChecked()
+ ? FLAG_ONGOING_SESSION
+ : 0;
+ flags |=
+ sessionManagedCheckBox.isChecked()
+ ? FLAG_ONGOING_SESSION_MANAGED
+ : 0;
+ flags |=
+ suggestedRouteCheckBox.isChecked() ? FLAG_SUGGESTED : 0;
+ RouteListingPreferenceItemSubtext subtext =
+ (RouteListingPreferenceItemSubtext)
+ subtextSpinner.getSelectedItem();
+ onEditRlpItemDialogAccepted(
+ item.mItem.getRouteId(),
+ item.mRouteName,
+ selectionBehavior.mConstant,
+ flags,
+ subtext.mConstant,
+ itemPositionInList);
+ })
+ .setNegativeButton("Dismiss", (unusedDialog, unusedWhich) -> {})
+ .create();
+
+ editRlpItemDialog.show();
+ }
+
+ private void onEditRlpItemDialogAccepted(
+ String routeId,
+ String routeName,
+ int selectionBehavior,
+ int flags,
+ int subtext,
+ int itemPositionInList) {
+ ArrayList<RouteListingPreferenceItemHolder> newRouteListingPreference =
+ new ArrayList<>(mRoutesManager.getRouteListingPreferenceItems());
+ RecyclerView.Adapter<?> adapter = mRouteListingPreferenceRecyclerView.getAdapter();
+ RouteListingPreference.Item.Builder newItemBuilder =
+ new RouteListingPreference.Item.Builder(routeId)
+ .setFlags(flags)
+ .setSelectionBehavior(selectionBehavior)
+ .setSubText(subtext);
+ if (subtext == SUBTEXT_CUSTOM) {
+ newItemBuilder.setCustomSubtextMessage("A custom subtext");
+ }
+ RouteListingPreference.Item newItem = newItemBuilder.build();
+ RouteListingPreferenceItemHolder newItemAndNamePair =
+ new RouteListingPreferenceItemHolder(newItem, routeName);
+ if (itemPositionInList < newRouteListingPreference.size()) {
+ newRouteListingPreference.set(itemPositionInList, newItemAndNamePair);
+ adapter.notifyItemChanged(itemPositionInList);
+ } else {
+ newRouteListingPreference.add(newItemAndNamePair);
+ adapter.notifyItemInserted(itemPositionInList);
+ }
+ mRoutesManager.setRouteListingPreferenceItems(newRouteListingPreference);
+ }
+
+ @NonNull
+ private ImmutableList<MediaRouter.RouteInfo> getRoutesWithNoAssociatedListingPreferenceItem() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return ImmutableList.of();
+ }
+ Set<String> routesWithAssociatedRouteListingPreferenceItem = new HashSet<>();
+ for (RouteListingPreferenceItemHolder element :
+ mRoutesManager.getRouteListingPreferenceItems()) {
+ String routeId = element.mItem.getRouteId();
+ routesWithAssociatedRouteListingPreferenceItem.add(routeId);
+ }
+
+ ImmutableList.Builder<MediaRouter.RouteInfo> resultBuilder = ImmutableList.builder();
+ for (MediaRouter.RouteInfo route : MediaRouter.getInstance(this).getRoutes()) {
+ if (!routesWithAssociatedRouteListingPreferenceItem.contains(route.getId())) {
+ resultBuilder.add(route);
+ }
+ }
+ return resultBuilder.build();
+ }
+
+ private class RecyclerViewCallback extends ItemTouchHelper.SimpleCallback {
+
+ private static final int INDEX_UNSET = -1;
+
+ private int mDraggingFromPosition;
+ private int mDraggingToPosition;
+
+ private RecyclerViewCallback() {
+ super(
+ ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+ ItemTouchHelper.START | ItemTouchHelper.END);
+ mDraggingFromPosition = INDEX_UNSET;
+ mDraggingToPosition = INDEX_UNSET;
+ }
+
+ @Override
+ public boolean onMove(
+ @NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder origin,
+ @NonNull RecyclerView.ViewHolder target) {
+ int fromPosition = origin.getBindingAdapterPosition();
+ int toPosition = target.getBindingAdapterPosition();
+ if (mDraggingFromPosition == INDEX_UNSET) {
+ // A drag has started, but we wait for the clearView() call to update the route
+ // listing preference.
+ mDraggingFromPosition = fromPosition;
+ }
+ mDraggingToPosition = toPosition;
+ recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
+ return false;
+ }
+
+ @Override
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
+ ArrayList<RouteListingPreferenceItemHolder> newRouteListingPreference =
+ new ArrayList<>(mRoutesManager.getRouteListingPreferenceItems());
+ int itemPosition = viewHolder.getBindingAdapterPosition();
+ newRouteListingPreference.remove(itemPosition);
+ mRoutesManager.setRouteListingPreferenceItems(newRouteListingPreference);
+ viewHolder.getBindingAdapter().notifyItemRemoved(itemPosition);
+ }
+
+ @Override
+ public void clearView(
+ @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+ if (mDraggingFromPosition != INDEX_UNSET) {
+ ArrayList<RouteListingPreferenceItemHolder> newRouteListingPreference =
+ new ArrayList<>(mRoutesManager.getRouteListingPreferenceItems());
+ newRouteListingPreference.add(
+ mDraggingToPosition,
+ newRouteListingPreference.remove(mDraggingFromPosition));
+ mRoutesManager.setRouteListingPreferenceItems(newRouteListingPreference);
+ }
+ mDraggingFromPosition = INDEX_UNSET;
+ mDraggingToPosition = INDEX_UNSET;
+ }
+ }
+
+ private class RouteListingPreferenceRecyclerViewAdapter
+ extends RecyclerView.Adapter<RecyclerViewItemViewHolder> {
+ @NonNull
+ @Override
+ public RecyclerViewItemViewHolder onCreateViewHolder(
+ @NonNull ViewGroup parent, int viewType) {
+ TextView textView =
+ (TextView)
+ LayoutInflater.from(parent.getContext())
+ .inflate(
+ android.R.layout.simple_list_item_1,
+ parent,
+ /* attachToRoot= */ false);
+ return new RecyclerViewItemViewHolder(textView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerViewItemViewHolder holder, int position) {
+ holder.mTextView.setText(
+ mRoutesManager.getRouteListingPreferenceItems().get(position).mRouteName);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mRoutesManager.getRouteListingPreferenceItems().size();
+ }
+ }
+
+ private class RecyclerViewItemViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ public final TextView mTextView;
+
+ private RecyclerViewItemViewHolder(TextView textView) {
+ super(textView);
+ mTextView = textView;
+ textView.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ setUpRouteListingPreferenceItemEditionDialog(getBindingAdapterPosition());
+ }
+ }
+
+ private enum RouteListingPreferenceItemSelectionBehavior {
+ SELECTION_BEHAVIOR_NONE(RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE, "None"),
+ SELECTION_BEHAVIOR_TRANSFER(
+ RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER, "Transfer"),
+ SELECTION_BEHAVIOR_GO_TO_APP(
+ RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP, "Go to app");
+
+ public final int mConstant;
+ public final String mHumanReadableString;
+
+ RouteListingPreferenceItemSelectionBehavior(
+ int constant, @NonNull String humanReadableString) {
+ mConstant = constant;
+ mHumanReadableString = humanReadableString;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return mHumanReadableString;
+ }
+
+ public static RouteListingPreferenceItemSelectionBehavior fromConstant(int constant) {
+ switch (constant) {
+ case RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE:
+ return SELECTION_BEHAVIOR_NONE;
+ case RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER:
+ return SELECTION_BEHAVIOR_TRANSFER;
+ case RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP:
+ return SELECTION_BEHAVIOR_GO_TO_APP;
+ default:
+ throw new IllegalArgumentException("Illegal selection behavior: " + constant);
+ }
+ }
+ }
+
+ private enum RouteListingPreferenceItemSubtext {
+ SUBTEXT_NONE(RouteListingPreference.Item.SUBTEXT_NONE, "None"),
+ SUBTEXT_ERROR_UNKNOWN(RouteListingPreference.Item.SUBTEXT_ERROR_UNKNOWN, "Unknown error"),
+ SUBTEXT_SUBSCRIPTION_REQUIRED(
+ RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED, "Subscription required"),
+ SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED(
+ RouteListingPreference.Item.SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED,
+ "Downloaded content disallowed"),
+ SUBTEXT_AD_ROUTING_DISALLOWED(
+ RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED, "Ad in progress"),
+ SUBTEXT_DEVICE_LOW_POWER(
+ RouteListingPreference.Item.SUBTEXT_DEVICE_LOW_POWER, "Device in low power mode"),
+ SUBTEXT_UNAUTHORIZED(RouteListingPreference.Item.SUBTEXT_UNAUTHORIZED, "Unauthorized"),
+ SUBTEXT_TRACK_UNSUPPORTED(
+ RouteListingPreference.Item.SUBTEXT_TRACK_UNSUPPORTED, "Track unsupported"),
+ SUBTEXT_CUSTOM(
+ RouteListingPreference.Item.SUBTEXT_CUSTOM, "Custom text (placeholder value)");
+
+ public final int mConstant;
+ @NonNull public final String mHumanReadableString;
+
+ RouteListingPreferenceItemSubtext(int constant, @NonNull String humanReadableString) {
+ mConstant = constant;
+ mHumanReadableString = humanReadableString;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return mHumanReadableString;
+ }
+
+ public static RouteListingPreferenceItemSubtext fromConstant(int constant) {
+ switch (constant) {
+ case RouteListingPreference.Item.SUBTEXT_NONE:
+ return SUBTEXT_NONE;
+ case RouteListingPreference.Item.SUBTEXT_ERROR_UNKNOWN:
+ return SUBTEXT_ERROR_UNKNOWN;
+ case RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED:
+ return SUBTEXT_SUBSCRIPTION_REQUIRED;
+ case RouteListingPreference.Item.SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED:
+ return SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED;
+ case RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED:
+ return SUBTEXT_AD_ROUTING_DISALLOWED;
+ case RouteListingPreference.Item.SUBTEXT_DEVICE_LOW_POWER:
+ return SUBTEXT_DEVICE_LOW_POWER;
+ case RouteListingPreference.Item.SUBTEXT_UNAUTHORIZED:
+ return SUBTEXT_UNAUTHORIZED;
+ case RouteListingPreference.Item.SUBTEXT_TRACK_UNSUPPORTED:
+ return SUBTEXT_TRACK_UNSUPPORTED;
+ case RouteListingPreference.Item.SUBTEXT_CUSTOM:
+ return SUBTEXT_CUSTOM;
+ default:
+ throw new IllegalArgumentException("Illegal subtext constant: " + constant);
+ }
+ }
+ }
+}
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
index 42b5973..2db33b8 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
@@ -26,6 +26,7 @@
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.Spinner;
import android.widget.Switch;
@@ -93,6 +94,13 @@
}
};
+ Button goToRouteListingPreferenceButton =
+ findViewById(R.id.go_to_route_listing_preference_button);
+ goToRouteListingPreferenceButton.setOnClickListener(
+ unusedView -> {
+ startActivity(new Intent(this, RouteListingPreferenceActivity.class));
+ });
+
RecyclerView routeList = findViewById(R.id.routes_recycler_view);
routeList.setLayoutManager(new LinearLayoutManager(/* context= */ this));
mRoutesAdapter = new RoutesAdapter(mRoutesManager.getRouteItems(), routeItemListener);
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/data/RouteItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/data/RouteItem.java
index eb034ed..5ac47e3 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/data/RouteItem.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/data/RouteItem.java
@@ -17,9 +17,9 @@
package com.example.androidx.mediarouting.data;
import android.media.AudioManager;
-import android.media.MediaRouter;
import androidx.annotation.NonNull;
+import androidx.mediarouter.media.MediaRouter;
import java.util.ArrayList;
import java.util.List;
@@ -40,6 +40,7 @@
private int mVolumeMax;
private DeviceType mDeviceType;
private List<String> mGroupMemberIds;
+ private boolean mIsSenderDriven;
public RouteItem() {
this.mId = UUID.randomUUID().toString();
@@ -54,6 +55,7 @@
this.mDeviceType = DeviceType.UNKNOWN;
this.mCanDisconnect = false;
this.mGroupMemberIds = new ArrayList<>();
+ this.mIsSenderDriven = false;
}
public RouteItem(
@@ -68,7 +70,8 @@
int volume,
int volumeMax,
@NonNull DeviceType deviceType,
- @NonNull List<String> groupMemberIds) {
+ @NonNull List<String> groupMemberIds,
+ boolean isSenderDriven) {
mId = id;
mName = name;
mDescription = description;
@@ -81,6 +84,7 @@
mVolumeMax = volumeMax;
mDeviceType = deviceType;
mGroupMemberIds = groupMemberIds;
+ mIsSenderDriven = isSenderDriven;
}
/** Returns a deep copy of an existing {@link RouteItem}. */
@@ -98,7 +102,8 @@
routeItem.getVolume(),
routeItem.getVolumeMax(),
routeItem.getDeviceType(),
- routeItem.getGroupMemberIds());
+ routeItem.getGroupMemberIds(),
+ routeItem.isSenderDriven());
}
public enum ControlFilter {
@@ -150,6 +155,14 @@
TV(MediaRouter.RouteInfo.DEVICE_TYPE_TV),
SPEAKER(MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER),
BLUETOOTH(MediaRouter.RouteInfo.DEVICE_TYPE_BLUETOOTH),
+ AUDIO_VIDEO_RECEIVER(MediaRouter.RouteInfo.DEVICE_TYPE_AUDIO_VIDEO_RECEIVER),
+ TABLET(MediaRouter.RouteInfo.DEVICE_TYPE_TABLET),
+ TABLET_DOCKED(MediaRouter.RouteInfo.DEVICE_TYPE_TABLET_DOCKED),
+ COMPUTER(MediaRouter.RouteInfo.DEVICE_TYPE_COMPUTER),
+ GAME_CONSOLE(MediaRouter.RouteInfo.DEVICE_TYPE_GAME_CONSOLE),
+ CAR(MediaRouter.RouteInfo.DEVICE_TYPE_CAR),
+ SMARTWATCH(MediaRouter.RouteInfo.DEVICE_TYPE_SMARTWATCH),
+ GROUP(MediaRouter.RouteInfo.DEVICE_TYPE_GROUP),
UNKNOWN(MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN);
public final int mIntConstant;
@@ -263,4 +276,12 @@
public void setGroupMemberIds(@NonNull List<String> groupMemberIds) {
mGroupMemberIds = groupMemberIds;
}
+
+ public boolean isSenderDriven() {
+ return mIsSenderDriven;
+ }
+
+ public void setSenderDriven(boolean isSenderDriven) {
+ mIsSenderDriven = isSenderDriven;
+ }
}
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
index daa2699..47ffed2 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
@@ -224,11 +224,17 @@
mGroupDescriptor = groupRouteBuilder.build();
mTvSelectedCount = countTvFromRoute(mGroupDescriptor);
- // Initialize DynamicRouteDescriptor with all the route descriptors.
+ RoutesManager routesManager = RoutesManager.getInstance(getContext());
+
+ // Initialize DynamicRouteDescriptor with all the non-sender-driven descriptors.
List<MediaRouteDescriptor> routeDescriptors = getDescriptor().getRoutes();
if (routeDescriptors != null && !routeDescriptors.isEmpty()) {
for (MediaRouteDescriptor descriptor: routeDescriptors) {
String routeId = descriptor.getId();
+ RouteItem item = routesManager.getRouteWithId(routeId);
+ if (item != null && item.isSenderDriven()) {
+ continue;
+ }
boolean selected = memberIds.contains(routeId);
DynamicRouteDescriptor.Builder builder =
new DynamicRouteDescriptor.Builder(descriptor)
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/ui/UiUtils.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/ui/UiUtils.java
new file mode 100644
index 0000000..7d3ff97
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/ui/UiUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.androidx.mediarouting.ui;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
+
+/** Contains utility methods related to UI management. */
+public final class UiUtils {
+
+ /**
+ * Populates the given {@link Spinner} using an {@link Enum} and its possible values.
+ *
+ * @param context The context in which the spinner is to be inflated.
+ * @param spinner The {@link Spinner} to populate.
+ * @param anEnum The initially selected value.
+ * @param selectionConsumer A consumer to invoke when an element is selected.
+ */
+ public static void setUpEnumBasedSpinner(
+ @NonNull Context context,
+ @NonNull Spinner spinner,
+ @NonNull Enum<?> anEnum,
+ @NonNull Consumer<Enum<?>> selectionConsumer) {
+ Enum<?>[] enumValues = anEnum.getDeclaringClass().getEnumConstants();
+ ArrayAdapter<Enum<?>> adapter =
+ new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, enumValues);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(adapter);
+ spinner.setSelection(anEnum.ordinal());
+
+ spinner.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(
+ AdapterView<?> adapterView, View view, int i, long l) {
+ selectionConsumer.accept(anEnum.getDeclaringClass().getEnumConstants()[i]);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {}
+ });
+ }
+
+ private UiUtils() {
+ // Prevent instantiation.
+ }
+}
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/activity_add_edit_route.xml b/samples/MediaRoutingDemo/src/main/res/layout/activity_add_edit_route.xml
index 8511ab91..58e0d57 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/activity_add_edit_route.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/activity_add_edit_route.xml
@@ -263,7 +263,7 @@
android:padding="4dp">
<Switch
- android:id="@+id/cam_disconnect_switch"
+ android:id="@+id/can_disconnect_switch"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
@@ -281,6 +281,31 @@
</RelativeLayout>
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <Switch
+ android:id="@+id/is_sender_driven_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="@string/is_sender_driven_switch_label" />
+
+ </RelativeLayout>
+
<Button
android:id="@+id/save_button"
android:layout_height="wrap_content"
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/activity_route_listing_preference.xml b/samples/MediaRoutingDemo/src/main/res/layout/activity_route_listing_preference.xml
new file mode 100644
index 0000000..b932051
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/layout/activity_route_listing_preference.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Enable Route Listing Preference" />
+
+ <Switch
+ android:id="@+id/enable_route_listing_preference_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Prefer system ordering" />
+
+ <Switch
+ android:id="@+id/prefer_system_ordering_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/route_listing_preference_recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/new_route_listing_preference_item_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="20dp"
+ android:padding="0dp"
+ app:srcCompat="@drawable/ic_add" />
+
+ </RelativeLayout>
+</LinearLayout>
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml b/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
index 5193b7c..2572fc6 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
@@ -29,6 +29,12 @@
android:layout_height="wrap_content"
android:text="@string/settings_activity_show_system_routes" />
+ <Button
+ android:id="@+id/go_to_route_listing_preference_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Route listing preference"/>
+
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
@@ -123,4 +129,4 @@
android:padding="0dp"
app:srcCompat="@drawable/ic_add" />
-</RelativeLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/activity_system_routing.xml b/samples/MediaRoutingDemo/src/main/res/layout/activity_system_routing.xml
index ceefa96..c3ef725 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/activity_system_routing.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/activity_system_routing.xml
@@ -24,4 +24,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
-</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
index 836a846bed4..3034f1d 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
@@ -100,3 +100,4 @@
</androidx.cardview.widget.CardView>
</FrameLayout>
+
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/route_listing_preference_item_dialog.xml b/samples/MediaRoutingDemo/src/main/res/layout/route_listing_preference_item_dialog.xml
new file mode 100644
index 0000000..3b2870b
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/layout/route_listing_preference_item_dialog.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Route name" />
+
+ <Spinner
+ android:id="@+id/rlp_item_dialog_route_name_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Selection behavior" />
+
+ <Spinner
+ android:id="@+id/rlp_item_dialog_selection_behavior_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Ongoing session" />
+
+ <CheckBox
+ android:id="@+id/rlp_item_dialog_ongoing_session_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Ongoing session managed" />
+
+ <CheckBox
+ android:id="@+id/rlp_item_dialog_session_managed_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Suggested route" />
+
+ <CheckBox
+ android:id="@+id/rlp_item_dialog_suggested_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_margin="12dp"
+ android:padding="4dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:text="Subtext" />
+
+ <Spinner
+ android:id="@+id/rlp_item_dialog_subtext_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/MediaRoutingDemo/src/main/res/values/strings.xml b/samples/MediaRoutingDemo/src/main/res/values/strings.xml
index 87596b2..f46edb8 100644
--- a/samples/MediaRoutingDemo/src/main/res/values/strings.xml
+++ b/samples/MediaRoutingDemo/src/main/res/values/strings.xml
@@ -49,12 +49,16 @@
<string name="dg_not_unselectable_route_name5"> Dynamic Route 5 - Not unselectable</string>
<string name="dg_static_group_route_name6"> Dynamic Route 6 - Static Group</string>
+ <string name="sender_driven_route_name1">Sender Driven TV 1</string>
+ <string name="sender_driven_route_name2">Sender Driven TV 2</string>
+
<string name="sample_media_route_provider_remote">Remote Playback (Simulated)</string>
<string name="sample_media_route_activity_local">Local Playback</string>
<string name="sample_media_route_activity_presentation">Local Playback on Presentation Display</string>
<string name="delete_route_alert_dialog_title">Delete this route?</string>
<string name="delete_route_alert_dialog_message">Are you sure you want to delete this route?</string>
+ <string name="is_sender_driven_switch_label">Is Sender Driven</string>
<string name="settings_activity_show_system_routes">Show system routes</string>
diff --git a/security/security-identity-credential/src/main/java/androidx/security/identity/HardwareIdentityCredential.java b/security/security-identity-credential/src/main/java/androidx/security/identity/HardwareIdentityCredential.java
index 855f060..096366c 100644
--- a/security/security-identity-credential/src/main/java/androidx/security/identity/HardwareIdentityCredential.java
+++ b/security/security-identity-credential/src/main/java/androidx/security/identity/HardwareIdentityCredential.java
@@ -249,6 +249,7 @@
return builder.build();
}
+ @SuppressWarnings("deprecation")
@Override
public void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
mCredential.setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
@@ -271,6 +272,7 @@
}
}
+ @SuppressWarnings("deprecation")
@Override
public @NonNull
int[] getAuthenticationDataUsageCount() {
diff --git a/settings.gradle b/settings.gradle
index f2f1fb6..be42b05 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -95,9 +95,9 @@
value("androidx.projects", getRequestedProjectSubsetName() ?: "Unset")
value("androidx.useMaxDepVersions", providers.gradleProperty("androidx.useMaxDepVersions").isPresent().toString())
- // Publish scan for androidx-main
- publishAlways()
- publishIfAuthenticated()
+ // Do not publish scan for androidx-platform-dev
+ // publishAlways()
+ // publishIfAuthenticated()
}
}
@@ -444,6 +444,7 @@
includeProject(":appsearch:appsearch-ktx", [BuildType.MAIN])
includeProject(":appsearch:appsearch-local-storage", [BuildType.MAIN])
includeProject(":appsearch:appsearch-platform-storage", [BuildType.MAIN])
+includeProject(":appsearch:appsearch-play-services-storage", [BuildType.MAIN])
includeProject(":appsearch:appsearch-test-util", [BuildType.MAIN])
includeProject(":arch:core:core-common", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
includeProject(":arch:core:core-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
@@ -676,6 +677,7 @@
includeProject(":core:core-i18n", [BuildType.MAIN])
includeProject(":core:core-ktx", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE])
includeProject(":core:core-location-altitude", [BuildType.MAIN])
+includeProject(":core:core-location-altitude-proto", [BuildType.MAIN])
includeProject(":core:core-performance", [BuildType.MAIN])
includeProject(":core:core-performance:core-performance-samples", "core/core-performance/samples", [BuildType.MAIN])
includeProject(":core:core-remoteviews", [BuildType.MAIN, BuildType.GLANCE])
@@ -684,6 +686,8 @@
includeProject(":core:core-splashscreen:core-splashscreen-samples", "core/core-splashscreen/samples", [BuildType.MAIN])
includeProject(":core:core-graphics-integration-tests:core-graphics-integration-tests", "core/core-graphics-integration-tests/testapp", [BuildType.MAIN])
includeProject(":core:core-role", [BuildType.MAIN])
+includeProject(":core:core-telecom", [BuildType.MAIN])
+includeProject(":core:core-telecom:integration-tests:testapp", [BuildType.MAIN])
includeProject(":core:uwb:uwb", [BuildType.MAIN])
includeProject(":core:uwb:uwb-rxjava3", [BuildType.MAIN])
includeProject(":credentials:credentials", [BuildType.MAIN])
@@ -758,6 +762,7 @@
includeProject(":glance:glance-wear-tiles", [BuildType.GLANCE])
includeProject(":glance:glance-wear-tiles-preview", [BuildType.GLANCE])
includeProject(":graphics:filters:filters", [BuildType.MAIN])
+includeProject(":graphics:graphics-path", [BuildType.MAIN])
includeProject(":graphics:graphics-core", [BuildType.MAIN])
includeProject(":graphics:graphics-shapes", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":graphics:integration-tests:testapp", [BuildType.MAIN])
@@ -1078,6 +1083,7 @@
includeProject(":window:window-testing", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
includeProject(":work:integration-tests:testapp", [BuildType.MAIN])
includeProject(":work:work-benchmark", [BuildType.MAIN])
+includeProject(":work:work-datatransfer", [BuildType.MAIN])
includeProject(":work:work-gcm", [BuildType.MAIN])
includeProject(":work:work-inspection", [BuildType.MAIN])
includeProject(":work:work-multiprocess", [BuildType.MAIN])
diff --git a/slidingpanelayout/slidingpanelayout/api/api_lint.ignore b/slidingpanelayout/slidingpanelayout/api/api_lint.ignore
index e495753..a288bd0 100644
--- a/slidingpanelayout/slidingpanelayout/api/api_lint.ignore
+++ b/slidingpanelayout/slidingpanelayout/api/api_lint.ignore
@@ -1,10 +1,6 @@
// Baseline format: 1.0
InvalidNullabilityOverride: androidx.slidingpanelayout.widget.SlidingPaneLayout#addView(android.view.View, int, android.view.ViewGroup.LayoutParams) parameter #0:
Invalid nullability on parameter `child` in method `addView`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.slidingpanelayout.widget.SlidingPaneLayout#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `c` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.slidingpanelayout.widget.SlidingPaneLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
- Invalid nullability on parameter `canvas` in method `drawChild`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: androidx.slidingpanelayout.widget.SlidingPaneLayout#removeView(android.view.View) parameter #0:
Invalid nullability on parameter `view` in method `removeView`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
@@ -15,6 +11,10 @@
MissingNullability: androidx.slidingpanelayout.widget.SlidingPaneLayout#checkLayoutParams(android.view.ViewGroup.LayoutParams) parameter #0:
Missing nullability on parameter `p` in method `checkLayoutParams`
+MissingNullability: androidx.slidingpanelayout.widget.SlidingPaneLayout#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `c` in method `draw`
+MissingNullability: androidx.slidingpanelayout.widget.SlidingPaneLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #0:
+ Missing nullability on parameter `canvas` in method `drawChild`
MissingNullability: androidx.slidingpanelayout.widget.SlidingPaneLayout#drawChild(android.graphics.Canvas, android.view.View, long) parameter #1:
Missing nullability on parameter `child` in method `drawChild`
MissingNullability: androidx.slidingpanelayout.widget.SlidingPaneLayout#generateDefaultLayoutParams():
diff --git a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/TestActivity.kt b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/TestActivity.kt
index 207861a..0e4d8ef 100644
--- a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/TestActivity.kt
+++ b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/helpers/TestActivity.kt
@@ -27,12 +27,14 @@
// callback when the activity created
onActivityCreated(this)
// disable enter animation
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
override fun finish() {
super.finish()
// disable exit animation
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
diff --git a/swiperefreshlayout/swiperefreshlayout/api/api_lint.ignore b/swiperefreshlayout/swiperefreshlayout/api/api_lint.ignore
index 6038f08..25a9da5 100644
--- a/swiperefreshlayout/swiperefreshlayout/api/api_lint.ignore
+++ b/swiperefreshlayout/swiperefreshlayout/api/api_lint.ignore
@@ -13,6 +13,8 @@
Internal field mOriginalOffsetTop must not be exposed
+MissingNullability: androidx.swiperefreshlayout.widget.CircularProgressDrawable#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.swiperefreshlayout.widget.CircularProgressDrawable#setColorFilter(android.graphics.ColorFilter) parameter #0:
Missing nullability on parameter `colorFilter` in method `setColorFilter`
MissingNullability: androidx.swiperefreshlayout.widget.SwipeRefreshLayout#onInterceptTouchEvent(android.view.MotionEvent) parameter #0:
diff --git a/testutils/testutils-runtime/src/androidTest/java/androidx/testutils/TestActivity.kt b/testutils/testutils-runtime/src/androidTest/java/androidx/testutils/TestActivity.kt
index f44d9c4..81334f8 100644
--- a/testutils/testutils-runtime/src/androidTest/java/androidx/testutils/TestActivity.kt
+++ b/testutils/testutils-runtime/src/androidTest/java/androidx/testutils/TestActivity.kt
@@ -32,6 +32,7 @@
super.onCreate(savedInstanceState)
setContentView(R.layout.content_view)
println("onCreate")
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
@@ -48,6 +49,7 @@
}
super.finish()
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
diff --git a/transition/transition/api/current.txt b/transition/transition/api/current.txt
index fdaded9..672d6db 100644
--- a/transition/transition/api/current.txt
+++ b/transition/transition/api/current.txt
@@ -146,6 +146,7 @@
method public String getName();
method public androidx.transition.PathMotion getPathMotion();
method public androidx.transition.TransitionPropagation? getPropagation();
+ method public final androidx.transition.Transition getRootTransition();
method public long getStartDelay();
method public java.util.List<java.lang.Integer!> getTargetIds();
method public java.util.List<java.lang.String!>? getTargetNames();
@@ -153,6 +154,7 @@
method public java.util.List<android.view.View!> getTargets();
method public String![]? getTransitionProperties();
method public androidx.transition.TransitionValues? getTransitionValues(android.view.View, boolean);
+ method public boolean isSeekingSupported();
method public boolean isTransitionRequired(androidx.transition.TransitionValues?, androidx.transition.TransitionValues?);
method public androidx.transition.Transition removeListener(androidx.transition.Transition.TransitionListener);
method public androidx.transition.Transition removeTarget(android.view.View);
@@ -180,9 +182,11 @@
public static interface Transition.TransitionListener {
method public void onTransitionCancel(androidx.transition.Transition);
method public void onTransitionEnd(androidx.transition.Transition);
+ method public default void onTransitionEnd(androidx.transition.Transition, boolean);
method public void onTransitionPause(androidx.transition.Transition);
method public void onTransitionResume(androidx.transition.Transition);
method public void onTransitionStart(androidx.transition.Transition);
+ method public default void onTransitionStart(androidx.transition.Transition, boolean);
}
public class TransitionInflater {
@@ -204,6 +208,7 @@
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
+ method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
method public static void endTransitions(android.view.ViewGroup?);
method public static void go(androidx.transition.Scene);
method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
@@ -219,6 +224,17 @@
method public abstract long getStartDelay(android.view.ViewGroup, androidx.transition.Transition, androidx.transition.TransitionValues?, androidx.transition.TransitionValues?);
}
+ public interface TransitionSeekController {
+ method public void addOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
+ method public void animateToEnd();
+ method public void animateToStart();
+ method public long getCurrentPlayTimeMillis();
+ method public long getDurationMillis();
+ method public boolean isReady();
+ method public void removeOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
+ method public void setCurrentPlayTimeMillis(long);
+ }
+
public class TransitionSet extends androidx.transition.Transition {
ctor public TransitionSet();
ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
diff --git a/transition/transition/api/restricted_current.txt b/transition/transition/api/restricted_current.txt
index a344c51..187290f 100644
--- a/transition/transition/api/restricted_current.txt
+++ b/transition/transition/api/restricted_current.txt
@@ -172,6 +172,7 @@
method public String getName();
method public androidx.transition.PathMotion getPathMotion();
method public androidx.transition.TransitionPropagation? getPropagation();
+ method public final androidx.transition.Transition getRootTransition();
method public long getStartDelay();
method public java.util.List<java.lang.Integer!> getTargetIds();
method public java.util.List<java.lang.String!>? getTargetNames();
@@ -179,6 +180,7 @@
method public java.util.List<android.view.View!> getTargets();
method public String![]? getTransitionProperties();
method public androidx.transition.TransitionValues? getTransitionValues(android.view.View, boolean);
+ method public boolean isSeekingSupported();
method public boolean isTransitionRequired(androidx.transition.TransitionValues?, androidx.transition.TransitionValues?);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void pause(android.view.View?);
method public androidx.transition.Transition removeListener(androidx.transition.Transition.TransitionListener);
@@ -213,9 +215,11 @@
public static interface Transition.TransitionListener {
method public void onTransitionCancel(androidx.transition.Transition);
method public void onTransitionEnd(androidx.transition.Transition);
+ method public default void onTransitionEnd(androidx.transition.Transition, boolean);
method public void onTransitionPause(androidx.transition.Transition);
method public void onTransitionResume(androidx.transition.Transition);
method public void onTransitionStart(androidx.transition.Transition);
+ method public default void onTransitionStart(androidx.transition.Transition, boolean);
}
public class TransitionInflater {
@@ -237,6 +241,7 @@
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
+ method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
method public static void endTransitions(android.view.ViewGroup?);
method public static void go(androidx.transition.Scene);
method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
@@ -252,6 +257,17 @@
method public abstract long getStartDelay(android.view.ViewGroup, androidx.transition.Transition, androidx.transition.TransitionValues?, androidx.transition.TransitionValues?);
}
+ public interface TransitionSeekController {
+ method public void addOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
+ method public void animateToEnd();
+ method public void animateToStart();
+ method public long getCurrentPlayTimeMillis();
+ method public long getDurationMillis();
+ method public boolean isReady();
+ method public void removeOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
+ method public void setCurrentPlayTimeMillis(long);
+ }
+
public class TransitionSet extends androidx.transition.Transition {
ctor public TransitionSet();
ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
diff --git a/transition/transition/build.gradle b/transition/transition/build.gradle
index 1518fbe..b899cd4 100644
--- a/transition/transition/build.gradle
+++ b/transition/transition/build.gradle
@@ -8,7 +8,7 @@
dependencies {
api("androidx.annotation:annotation:1.2.0")
- api("androidx.core:core:1.1.0")
+ api(project(":core:core"))
implementation("androidx.collection:collection:1.1.0")
compileOnly("androidx.fragment:fragment:1.2.5")
compileOnly("androidx.appcompat:appcompat:1.0.1")
@@ -22,6 +22,7 @@
androidTestImplementation(libs.espressoCore, excludes.espresso)
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+ androidTestImplementation(libs.opentest4j)
androidTestImplementation(project(":fragment:fragment"))
androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
androidTestImplementation(project(":internal-testutils-runtime"), {
diff --git a/transition/transition/src/androidTest/java/androidx/transition/AlwaysTransition.kt b/transition/transition/src/androidTest/java/androidx/transition/AlwaysTransition.kt
new file mode 100644
index 0000000..0ac5f03
--- /dev/null
+++ b/transition/transition/src/androidTest/java/androidx/transition/AlwaysTransition.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.transition
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.view.ViewGroup
+
+/**
+ * A test transition that always provides an animation, regardless of start/end state
+ */
+class AlwaysTransition(private val keyPrefix: String) : Transition() {
+ override fun captureStartValues(transitionValues: TransitionValues) {
+ transitionValues.values[keyPrefix + Key] = AlwaysChangingValue++
+ }
+
+ override fun captureEndValues(transitionValues: TransitionValues) {
+ transitionValues.values[keyPrefix + Key] = AlwaysChangingValue++
+ }
+
+ override fun isSeekingSupported(): Boolean = true
+
+ override fun createAnimator(
+ sceneRoot: ViewGroup,
+ startValues: TransitionValues?,
+ endValues: TransitionValues?
+ ): Animator = ValueAnimator.ofFloat(0f, 100f)
+
+ companion object {
+ private const val Key = "alwaysChanging"
+ private var AlwaysChangingValue = 0
+ }
+}
\ No newline at end of file
diff --git a/transition/transition/src/androidTest/java/androidx/transition/BaseTransitionTest.java b/transition/transition/src/androidTest/java/androidx/transition/BaseTransitionTest.java
index a5b727f..e2ed58e 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/BaseTransitionTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/BaseTransitionTest.java
@@ -17,7 +17,7 @@
package androidx.transition;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -55,7 +55,7 @@
mRoot = (LinearLayout) rule.getActivity().findViewById(R.id.root);
mTransitionTargets.clear();
mTransition = createTransition();
- mListener = mock(Transition.TransitionListener.class);
+ mListener = spy(new TransitionListenerAdapter());
mTransition.addListener(mListener);
}
@@ -113,7 +113,7 @@
void resetListener() {
mTransition.removeListener(mListener);
- mListener = mock(Transition.TransitionListener.class);
+ mListener = spy(new TransitionListenerAdapter());
mTransition.addListener(mListener);
}
diff --git a/transition/transition/src/androidTest/java/androidx/transition/ChangeBoundsTest.java b/transition/transition/src/androidTest/java/androidx/transition/ChangeBoundsTest.java
index c1a8962..3206c710 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/ChangeBoundsTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/ChangeBoundsTest.java
@@ -19,23 +19,33 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.graphics.Rect;
import android.os.Build;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.core.os.BuildCompat;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.transition.test.R;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@LargeTest
public class ChangeBoundsTest extends BaseTransitionTest {
@@ -113,7 +123,592 @@
suppressLayout.ensureExpectedValueApplied();
}
- private class TestSuppressLayout extends FrameLayout {
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingChangeBoundsNoClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+
+ final View view = viewArr[0];
+ ViewGroup parent = (ViewGroup) view.getParent();
+
+ rule.runOnUiThread(() -> {
+ assertEquals(100, view.getWidth());
+ assertEquals(100, view.getHeight());
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 200;
+ layoutParams.height = 300;
+ view.setLayoutParams(layoutParams);
+ });
+ final TransitionSeekController seekController = seekControllerArr[0];
+ CountDownLatch endLatch = new CountDownLatch(1);
+
+ rule.runOnUiThread(() -> {
+ assertEquals(100, view.getWidth());
+ assertEquals(100, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek past the always there transition before the change bounds
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(100, view.getWidth());
+ assertEquals(100, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek to half through the change bounds
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(150, view.getWidth());
+ assertEquals(200, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek past the ChangeBounds
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(200, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek back to half through the change bounds
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(150, view.getWidth());
+ assertEquals(200, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek before the change bounds:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(100, view.getWidth());
+ assertEquals(100, view.getHeight());
+ assertTrue(parent.isLayoutSuppressed());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ ChangeBounds returnTransition = new ChangeBounds();
+ returnTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ endLatch.countDown();
+ }
+ });
+ TransitionManager.beginDelayedTransition(mRoot, returnTransition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 100;
+ layoutParams.height = 100;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 150x200 through and head toward 100x100
+ assertTrue(150 >= view.getWidth());
+ assertTrue(200 >= view.getHeight());
+ });
+
+ assertTrue(endLatch.await(3, TimeUnit.SECONDS));
+ rule.runOnUiThread(() -> {
+ assertFalse(parent.isLayoutSuppressed());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingChangeBoundsWithClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ changeBounds.setResizeClip(true);
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+ ViewGroup parent = (ViewGroup) view.getParent();
+
+ rule.runOnUiThread(() -> {
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 200;
+ view.setLayoutParams(layoutParams);
+ });
+ final TransitionSeekController seekController = seekControllerArr[0];
+ CountDownLatch endLatch = new CountDownLatch(1);
+
+ rule.runOnUiThread(() -> {
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertEquals(new Rect(0, 0, 400, 300), view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek past the always there transition before the change bounds
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertEquals(new Rect(0, 0, 400, 300), view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek to half through the change bounds
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertEquals(new Rect(0, 0, 350, 250), view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek past the ChangeBounds
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(300, view.getWidth());
+ assertEquals(200, view.getHeight());
+ assertNull(view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek back to half through the change bounds
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertEquals(new Rect(0, 0, 350, 250), view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ // Seek before the change bounds:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+ assertNull(view.getClipBounds());
+ assertTrue(parent.isLayoutSuppressed());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ ChangeBounds returnTransition = new ChangeBounds();
+ returnTransition.setResizeClip(true);
+ returnTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ endLatch.countDown();
+ }
+ });
+ TransitionManager.beginDelayedTransition(mRoot, returnTransition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 200;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 400x500, clipped to 350x250
+ assertEquals(400, view.getWidth());
+ assertEquals(500, view.getHeight());
+ assertTrue(view.getClipBounds().width() <= 350);
+ assertTrue(view.getClipBounds().height() >= 250);
+ });
+
+ assertTrue(endLatch.await(3, TimeUnit.SECONDS));
+ rule.runOnUiThread(() -> {
+ assertEquals(200, view.getWidth());
+ assertEquals(500, view.getHeight());
+ assertFalse(parent.isLayoutSuppressed());
+ assertNull(view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void interruptedBeforeStartNoClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ ChangeBounds change = new ChangeBounds();
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, change);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 100;
+ layoutParams.height = 150;
+ view.setLayoutParams(layoutParams);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ // It should start from 400x300
+ assertEquals(400, view.getWidth());
+ assertEquals(300, view.getHeight());
+
+ // go halfway through
+ seekController.setCurrentPlayTimeMillis(150);
+ assertEquals(250, view.getWidth());
+ assertEquals(225, view.getHeight());
+
+ // skip to the end
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(100, view.getWidth());
+ assertEquals(150, view.getHeight());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void interruptedBeforeStartWithClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ changeBounds.setResizeClip(true);
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ ChangeBounds change = new ChangeBounds();
+ change.setResizeClip(true);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, change);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 600;
+ layoutParams.height = 150;
+ view.setLayoutParams(layoutParams);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ // It should start from 600x500, clipped to 400x300
+ assertEquals(600, view.getWidth());
+ assertEquals(500, view.getHeight());
+ assertEquals(400, view.getClipBounds().width());
+ assertEquals(300, view.getClipBounds().height());
+
+ // go halfway through
+ seekController.setCurrentPlayTimeMillis(150);
+ assertEquals(500, view.getClipBounds().width());
+ assertEquals(225, view.getClipBounds().height());
+
+ // skip to the end
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(600, view.getWidth());
+ assertEquals(150, view.getHeight());
+ assertNull(view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void interruptedAfterEndNoClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+ TransitionSeekController seekController1 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekController1.setCurrentPlayTimeMillis(800);
+ ChangeBounds change = new ChangeBounds();
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, change);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 100;
+ layoutParams.height = 150;
+ view.setLayoutParams(layoutParams);
+ });
+
+ final TransitionSeekController seekController2 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ // It should start from 300x500
+ assertEquals(300, view.getWidth());
+ assertEquals(500, view.getHeight());
+
+ // go halfway through
+ seekController2.setCurrentPlayTimeMillis(150);
+ assertEquals(200, view.getWidth());
+ assertEquals(325, view.getHeight());
+
+ // skip to the end
+ seekController2.setCurrentPlayTimeMillis(300);
+ assertEquals(100, view.getWidth());
+ assertEquals(150, view.getHeight());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void interruptedAfterEndWithClip() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ changeBounds.setResizeClip(true);
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+ TransitionSeekController seekController1 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekController1.setCurrentPlayTimeMillis(800);
+ ChangeBounds change = new ChangeBounds();
+ change.setResizeClip(true);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, change);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 100;
+ layoutParams.height = 150;
+ view.setLayoutParams(layoutParams);
+ });
+
+ final TransitionSeekController seekController2 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ // It should start from 300x500, clipped to 300x500
+ assertEquals(300, view.getWidth());
+ assertEquals(500, view.getHeight());
+ assertEquals(300, view.getClipBounds().width());
+ assertEquals(500, view.getClipBounds().height());
+
+ // go halfway through
+ seekController2.setCurrentPlayTimeMillis(150);
+ assertEquals(200, view.getClipBounds().width());
+ assertEquals(325, view.getClipBounds().height());
+
+ // skip to the end
+ seekController2.setCurrentPlayTimeMillis(300);
+ assertEquals(100, view.getWidth());
+ assertEquals(150, view.getHeight());
+ assertNull(view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void startTransitionAfterSeeking() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ ChangeBounds changeBounds = new ChangeBounds();
+ changeBounds.setResizeClip(true);
+ transition.addTransition(changeBounds);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+ TransitionSeekController seekController1 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekController1.setCurrentPlayTimeMillis(900);
+ seekController1.setCurrentPlayTimeMillis(0);
+
+ ChangeBounds change = new ChangeBounds();
+ change.setResizeClip(true);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, change);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 100;
+ layoutParams.height = 150;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(400, view.getClipBounds().width());
+ assertEquals(300, view.getClipBounds().height());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekNoChange() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(400, 300));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+ TransitionSeekController seekController1 = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekController1.setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] =
+ TransitionManager.controlDelayedTransition(mRoot, new ChangeBounds());
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.width = 300;
+ layoutParams.height = 500;
+ view.setLayoutParams(layoutParams);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0, seekControllerArr[0].getDurationMillis());
+ assertEquals(350, view.getWidth());
+ assertEquals(400, view.getHeight());
+
+ // First seek controls the transition
+ seekController1.setCurrentPlayTimeMillis(900);
+ assertEquals(300, view.getWidth());
+ assertEquals(500, view.getHeight());
+ });
+ }
+
+ private static class TestSuppressLayout extends FrameLayout {
private boolean mExpectedSuppressLayout;
private Boolean mActualSuppressLayout;
diff --git a/transition/transition/src/androidTest/java/androidx/transition/ChangeClipBoundsTest.java b/transition/transition/src/androidTest/java/androidx/transition/ChangeClipBoundsTest.java
index fc3808d5..f255f62 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/ChangeClipBoundsTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/ChangeClipBoundsTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -27,8 +28,11 @@
import android.graphics.Color;
import android.graphics.Rect;
+import android.os.Build;
import android.view.View;
+import android.view.ViewGroup;
+import androidx.core.os.BuildCompat;
import androidx.core.view.ViewCompat;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SdkSuppress;
@@ -36,6 +40,9 @@
import org.junit.Test;
import org.mockito.ArgumentMatcher;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@LargeTest
public class ChangeClipBoundsTest extends BaseTransitionTest {
@@ -103,6 +110,407 @@
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingClipToNull() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ viewArr[0].setClipBounds(new Rect(0, 0, 50, 50));
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+ final View view = viewArr[0];
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(null);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+ ChangeClipBounds returnTransition = new ChangeClipBounds();
+ CountDownLatch latch = new CountDownLatch(1);
+ returnTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ latch.countDown();
+ }
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek past the always there transition before the clip transition
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 75, 75), view.getClipBounds());
+
+ // Seek past the transition
+ seekController.setCurrentPlayTimeMillis(800);
+ assertNull(view.getClipBounds());
+
+ // Seek back to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 75, 75), view.getClipBounds());
+
+ // Seek before the transition:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, returnTransition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 75x75 and then transition in
+ assertTrue(view.getClipBounds().width() <= 75);
+ assertTrue(view.getClipBounds().height() <= 75);
+ });
+
+ assertTrue(latch.await(3, TimeUnit.SECONDS));
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingClipFromNull() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+ final View view = viewArr[0];
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+ ChangeClipBounds returnTransition = new ChangeClipBounds();
+ CountDownLatch latch = new CountDownLatch(1);
+ returnTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ latch.countDown();
+ }
+ });
+
+ rule.runOnUiThread(() -> {
+ assertNull(view.getClipBounds());
+
+ // Seek past the always there transition before the clip transition
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(new Rect(0, 0, 100, 100), view.getClipBounds());
+
+ // Seek to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 75, 75), view.getClipBounds());
+
+ // Seek past the transition
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek back to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 75, 75), view.getClipBounds());
+
+ // Seek before the transition:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertNull(view.getClipBounds());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, returnTransition);
+ view.setClipBounds(null);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 75x75 and then transition in
+ assertTrue(view.getClipBounds().width() >= 75);
+ assertTrue(view.getClipBounds().height() >= 75);
+ });
+
+ assertTrue(latch.await(3, TimeUnit.SECONDS));
+ rule.runOnUiThread(() -> {
+ assertNull(view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingClips() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ viewArr[0].setClipBounds(new Rect(0, 0, 50, 50));
+ });
+ final View view = viewArr[0];
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 80, 80));
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+ ChangeClipBounds returnTransition = new ChangeClipBounds();
+ CountDownLatch latch = new CountDownLatch(1);
+ returnTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ latch.countDown();
+ }
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek past the always there transition before the clip transition
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 65, 65), view.getClipBounds());
+
+ // Seek past the transition
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(new Rect(0, 0, 80, 80), view.getClipBounds());
+
+ // Seek back to half through the transition
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(new Rect(0, 0, 65, 65), view.getClipBounds());
+
+ // Seek before the transition:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, returnTransition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 75x75 and then transition in
+ assertTrue(view.getClipBounds().width() <= 65);
+ assertTrue(view.getClipBounds().height() <= 65);
+ });
+
+ assertTrue(latch.await(3, TimeUnit.SECONDS));
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void changeClipBeforeStart() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ viewArr[0].setClipBounds(new Rect(0, 0, 20, 50));
+ });
+ final View view = viewArr[0];
+ rule.runOnUiThread(() -> {
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 80, 80));
+ });
+
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] =
+ TransitionManager.controlDelayedTransition(mRoot, new ChangeClipBounds());
+ view.setClipBounds(new Rect(0, 0, 100, 100));
+ });
+
+ rule.runOnUiThread(() -> {
+ TransitionSeekController seekController = seekControllerArr[0];
+ // It should start from 20x50 and go to 100x100
+ assertEquals(20, view.getClipBounds().width());
+ assertEquals(50, view.getClipBounds().height());
+
+ // half way through
+ seekController.setCurrentPlayTimeMillis(150);
+ assertEquals(60, view.getClipBounds().width());
+ assertEquals(75, view.getClipBounds().height());
+
+ // finish
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(100, view.getClipBounds().width());
+ assertEquals(100, view.getClipBounds().height());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void testSeekInterruption() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // supported on U+
+ }
+ final View view = new View(rule.getActivity());
+
+ rule.runOnUiThread(() -> {
+ mRoot.addView(view, new ViewGroup.LayoutParams(100, 100));
+ });
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ rule.runOnUiThread(() -> {
+ assertNull(view.getClipBounds());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+
+ // Seek back to the beginning
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ assertNull(view.getClipBounds());
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(50, 50, 100, 100));
+ });
+
+ rule.runOnUiThread(() -> {
+ assertNull(view.getClipBounds());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(new Rect(50, 50, 100, 100), view.getClipBounds());
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(null);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(50, 50, 100, 100), view.getClipBounds());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertNull(view.getClipBounds());
+
+ // Seek to the middle
+ seekControllerArr[0].setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(new Rect(25, 25, 100, 100), view.getClipBounds());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void testSeekNoChange() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // supported on U+
+ }
+ final View view = new View(rule.getActivity());
+
+ rule.runOnUiThread(() -> {
+ mRoot.addView(view, new ViewGroup.LayoutParams(100, 100));
+ });
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeClipBounds());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ TransitionSeekController firstController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] =
+ TransitionManager.controlDelayedTransition(mRoot, new ChangeClipBounds());
+ view.setClipBounds(new Rect(0, 0, 50, 50));
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0, seekControllerArr[0].getDurationMillis());
+
+ // Should only be controlled by the first transition
+ firstController.setCurrentPlayTimeMillis(900);
+ assertEquals(new Rect(0, 0, 50, 50), view.getClipBounds());
+ });
+ }
+
private ArgumentMatcher<Rect> isRectContaining(final Rect rect) {
return new ArgumentMatcher<Rect>() {
@Override
diff --git a/transition/transition/src/androidTest/java/androidx/transition/ChangeImageTransformTest.java b/transition/transition/src/androidTest/java/androidx/transition/ChangeImageTransformTest.java
index 8ddefd7..ebff1c4 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/ChangeImageTransformTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/ChangeImageTransformTest.java
@@ -40,7 +40,9 @@
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.transition.test.R;
@@ -153,6 +155,88 @@
assertEquals(ImageView.ScaleType.CENTER_CROP, imageView.getScaleType());
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void testSeekInterruption() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final ImageView imageView = enterImageViewScene(ImageView.ScaleType.FIT_START,
+ null, false);
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeImageTransform());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+ });
+
+ rule.runOnUiThread(() -> {
+ verifyMatrixMatches(fitStartMatrix(), getDrawMatrixCompat(imageView));
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ verifyMatrixMatches(fitXYMatrix(), getDrawMatrixCompat(imageView));
+ // Seek back to the beginning
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ verifyMatrixMatches(fitStartMatrix(), getDrawMatrixCompat(imageView));
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ imageView.setScaleType(ImageView.ScaleType.FIT_END);
+ });
+
+ rule.runOnUiThread(() -> {
+ verifyMatrixMatches(fitStartMatrix(), getDrawMatrixCompat(imageView));
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ verifyMatrixMatches(fitEndMatrix(), getDrawMatrixCompat(imageView));
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ imageView.setScaleType(ImageView.ScaleType.FIT_START);
+ });
+
+ rule.runOnUiThread(() -> {
+ verifyMatrixMatches(fitEndMatrix(), getDrawMatrixCompat(imageView));
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ verifyMatrixMatches(fitStartMatrix(), getDrawMatrixCompat(imageView));
+
+ // Seek to the middle
+ seekControllerArr[0].setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+ });
+
+ rule.runOnUiThread(() -> {
+ verifyMatrixMatches(betweenStartAndEnd(), getDrawMatrixCompat(imageView));
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ verifyMatrixMatches(fitXYMatrix(), getDrawMatrixCompat(imageView));
+ });
+ }
+
+ private Matrix betweenStartAndEnd() {
+ Matrix start = fitStartMatrix();
+ float[] startVals = new float[9];
+ start.getValues(startVals);
+ Matrix end = fitEndMatrix();
+ float[] endVals = new float[9];
+ end.getValues(endVals);
+
+ float[] middleVals = new float[9];
+ for (int i = 0; i < 9; i++) {
+ middleVals[i] = (startVals[i] + endVals[i]) / 2f;
+ }
+ Matrix middle = new Matrix();
+ middle.setValues(middleVals);
+ return middle;
+ }
+
private Matrix centerMatrix() {
int imageWidth = mImage.getIntrinsicWidth();
int imageViewWidth = mImageView.getWidth();
diff --git a/transition/transition/src/androidTest/java/androidx/transition/ChangeScrollTest.java b/transition/transition/src/androidTest/java/androidx/transition/ChangeScrollTest.java
index 3f94b5a..68414e5 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/ChangeScrollTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/ChangeScrollTest.java
@@ -19,15 +19,21 @@
import static androidx.transition.AtLeastOnceWithin.atLeastOnceWithin;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.and;
import static org.mockito.AdditionalMatchers.gt;
import static org.mockito.AdditionalMatchers.leq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.transition.test.R;
@@ -76,4 +82,193 @@
});
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingScroll() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeScroll());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(100);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0, view.getScrollY());
+
+ // Seek past the always there transition before the scroll
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(0, view.getScrollY());
+
+ // Seek to half through the scroll
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(50, view.getScrollY());
+
+ // Seek past the scroll
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(100, view.getScrollY());
+
+ // Seek back to half through the scroll
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(50, view.getScrollY());
+
+ // Seek before the scroll:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(0, view.getScrollY());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, new ChangeScroll());
+ view.setScrollY(0);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 50 and move to 0
+ assertTrue(view.getScrollY() <= 50);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingScrollBeforeStart() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeScroll());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ viewArr[0].setScrollY(100);
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(200);
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] =
+ TransitionManager.controlDelayedTransition(mRoot, new ChangeScroll());
+ view.setScrollY(0);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ // Should start from 100 and go to 0
+ assertEquals(100, view.getScrollY());
+
+ // Seek to half through the scroll
+ seekController.setCurrentPlayTimeMillis(150);
+ assertEquals(50, view.getScrollY());
+
+ // Seek to the end
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(0, view.getScrollY());
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void testSeekInterruption() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final View view = new View(rule.getActivity());
+
+ rule.runOnUiThread(() -> {
+ mRoot.addView(view, new ViewGroup.LayoutParams(100, 100));
+ });
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new ChangeScroll());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(100);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0, view.getScrollY());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(100, view.getScrollY());
+
+ // Seek back to the beginning
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ assertEquals(0, view.getScrollY());
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(200);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0, view.getScrollY());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(200, view.getScrollY());
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(50);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(200, view.getScrollY());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(50, view.getScrollY());
+
+ // Seek to the middle
+ seekControllerArr[0].setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setScrollY(500);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals((200 + 50) / 2, view.getScrollY());
+
+ // Seek to the end
+ seekControllerArr[0].setCurrentPlayTimeMillis(900);
+ assertEquals(500, view.getScrollY());
+ });
+ }
}
diff --git a/transition/transition/src/androidTest/java/androidx/transition/ExplodeTest.java b/transition/transition/src/androidTest/java/androidx/transition/ExplodeTest.java
index 34954a9..7a9fc71 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/ExplodeTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/ExplodeTest.java
@@ -20,15 +20,22 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.graphics.Color;
+import android.os.Build;
import android.view.Gravity;
import android.view.View;
+import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import org.hamcrest.Description;
@@ -176,6 +183,279 @@
assertThat(Arrays.asList(1f, 9f, 3f), is(not(decreasing())));
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingExplode() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new Explode());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ this.mRedSquare.setVisibility(View.GONE);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ float[] translationValues = new float[2];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, ViewUtils.getTransitionAlpha(mRedSquare), 0f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+
+ // Seek past the always there transition before the explode
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+
+ // Seek half way:
+ seekController.setCurrentPlayTimeMillis(450);
+ assertNotEquals(0f, mRedSquare.getTranslationX(), 0.01f);
+ assertNotEquals(0f, mRedSquare.getTranslationY(), 0.01f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ translationValues[0] = mRedSquare.getTranslationX();
+ translationValues[1] = mRedSquare.getTranslationY();
+
+ // Seek past the end
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+ assertEquals(View.GONE, mRedSquare.getVisibility());
+
+ // Seek before the explode:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+
+ seekController.setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and decrease
+ assertEquals(translationValues[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(translationValues[1], mRedSquare.getTranslationY(), 1f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingImplode() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ Explode implode = new Explode();
+ implode.setInterpolator(new LinearInterpolator());
+ transition.addTransition(implode);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ rule.runOnUiThread(() -> {
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ mRedSquare.setVisibility(View.VISIBLE);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ float[] translationValues = new float[2];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, ViewUtils.getTransitionAlpha(mRedSquare), 0f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertNotEquals(0f, mRedSquare.getTranslationX(), 0.01f);
+ assertNotEquals(0f, mRedSquare.getTranslationY(), 0.01f);
+
+ float startX = mRedSquare.getTranslationX();
+ float startY = mRedSquare.getTranslationY();
+
+ // Seek past the always there transition before the explode
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(startX, mRedSquare.getTranslationX(), 0f);
+ assertEquals(startY, mRedSquare.getTranslationY(), 0f);
+
+ // Seek half way:
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(startX / 2f, mRedSquare.getTranslationX(), 0.01f);
+ assertEquals(startY / 2f, mRedSquare.getTranslationY(), 0.01f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ translationValues[0] = mRedSquare.getTranslationX();
+ translationValues[1] = mRedSquare.getTranslationY();
+
+ // Seek past the end
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+
+ // Seek before the explode:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+ assertEquals(startX, mRedSquare.getTranslationX(), 0f);
+ assertEquals(startY, mRedSquare.getTranslationY(), 0f);
+
+ seekController.setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and increase
+ assertEquals(translationValues[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(translationValues[1], mRedSquare.getTranslationY(), 1f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(View.GONE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingImplodeBeforeStart() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ Explode implode = new Explode();
+ implode.setInterpolator(new LinearInterpolator());
+ transition.addTransition(implode);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ rule.runOnUiThread(() -> {
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ mRedSquare.setVisibility(View.VISIBLE);
+ });
+
+ float[] translationValues = new float[2];
+
+ rule.runOnUiThread(() -> {
+ translationValues[0] = mRedSquare.getTranslationX();
+ translationValues[1] = mRedSquare.getTranslationY();
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from all the way out
+ assertEquals(translationValues[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(translationValues[1], mRedSquare.getTranslationY(), 1f);
+ assertEquals(View.VISIBLE, mRedSquare.getVisibility());
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(View.GONE, mRedSquare.getVisibility());
+ assertEquals(0f, mRedSquare.getTranslationX(), 0f);
+ assertEquals(0f, mRedSquare.getTranslationY(), 0f);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekWithTranslation() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ rule.runOnUiThread(() -> {
+ mRedSquare.setTranslationX(1f);
+ mRedSquare.setTranslationY(5f);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ final float[] interruptedTranslation = new float[2];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, mRedSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, mRedSquare.getTranslationY(), 0.01f);
+
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(150);
+ interruptedTranslation[0] = mRedSquare.getTranslationX();
+ interruptedTranslation[1] = mRedSquare.getTranslationY();
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and increase
+ assertEquals(interruptedTranslation[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(interruptedTranslation[1], mRedSquare.getTranslationY(), 1f);
+
+ // make sure it would go to the start value
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(1f, mRedSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, mRedSquare.getTranslationY(), 0.01f);
+
+ // Now go back to the interrupted position again:
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ assertEquals(interruptedTranslation[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(interruptedTranslation[1], mRedSquare.getTranslationY(), 1f);
+
+ // Send it back to GONE
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(interruptedTranslation[0], mRedSquare.getTranslationX(), 1f);
+ assertEquals(interruptedTranslation[1], mRedSquare.getTranslationY(), 1f);
+
+ // it should move away (toward the top-left)
+ seekControllerArr[0].setCurrentPlayTimeMillis(299);
+ assertTrue(mRedSquare.getTranslationX() < interruptedTranslation[0]);
+ assertTrue(mRedSquare.getTranslationY() < interruptedTranslation[1]);
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Explode());
+ mRedSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should end up at the initial translation
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(1f, mRedSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, mRedSquare.getTranslationY(), 0.01f);
+ });
+ }
+
private Matcher<View> hasVisibility(final int visibility) {
return new TypeSafeMatcher<View>() {
@Override
diff --git a/transition/transition/src/androidTest/java/androidx/transition/FadeTest.java b/transition/transition/src/androidTest/java/androidx/transition/FadeTest.java
index 16a9bbe..a55c8c5 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/FadeTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/FadeTest.java
@@ -25,9 +25,11 @@
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -40,8 +42,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.testutils.AnimationDurationScaleRule;
import androidx.transition.test.R;
@@ -126,7 +130,7 @@
float[] valuesOut = new float[2];
final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, interrupt,
valuesOut);
- final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerOut = spy(new TransitionListenerAdapter());
fadeOut.addListener(listenerOut);
changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
@@ -137,7 +141,7 @@
// Fade in
float[] valuesIn = new float[2];
final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, null, valuesIn);
- final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerIn = spy(new TransitionListenerAdapter());
fadeIn.addListener(listenerIn);
changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
verify(listenerOut, timeout(3000)).onTransitionPause(any(Transition.class));
@@ -162,7 +166,7 @@
final Runnable interrupt = mock(Runnable.class);
float[] valuesIn = new float[2];
final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, interrupt, valuesIn);
- final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerIn = spy(new TransitionListenerAdapter());
fadeIn.addListener(listenerIn);
changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
@@ -173,7 +177,7 @@
// Fade out
float[] valuesOut = new float[2];
final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, null, valuesOut);
- final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerOut = spy(new TransitionListenerAdapter());
fadeOut.addListener(listenerOut);
changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
verify(listenerIn, timeout(3000)).onTransitionPause(any(Transition.class));
@@ -199,14 +203,14 @@
});
// Fade out
final Fade fadeOut = new Fade(Fade.OUT);
- final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerOut = spy(new TransitionListenerAdapter());
fadeOut.addListener(listenerOut);
changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
// Fade in
final Fade fadeIn = new Fade(Fade.IN);
- final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listenerIn = spy(new TransitionListenerAdapter());
fadeIn.addListener(listenerIn);
changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
@@ -237,7 +241,7 @@
// We don't really care how short the duration is, so let's make it really short
final Fade fade = new Fade();
fade.setDuration(1);
- Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ Transition.TransitionListener listener = spy(new TransitionListenerAdapter());
fade.addListener(listener);
rule.runOnUiThread(new Runnable() {
@@ -262,6 +266,194 @@
assertNotNull(activity.findViewById(R.id.redSquare));
}
+ @Test
+ public void seekingFadeIn() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new Fade());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+
+ final View view = viewArr[0];
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(0f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+
+ // Seek past the always there transition before the fade
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(0f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.LAYER_TYPE_HARDWARE, view.getLayerType());
+
+ // Seek to half through the fade
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(0.5f, ViewUtils.getTransitionAlpha(view), 0.01f);
+
+ // Seek past the fade
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+ assertEquals(1f, ViewUtils.getTransitionAlpha(view), 0f);
+
+ // Seek back to half through the fade
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(View.LAYER_TYPE_HARDWARE, view.getLayerType());
+ assertEquals(0.5f, ViewUtils.getTransitionAlpha(view), 0f);
+
+ // Seek before the fade:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(0f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, new Fade());
+ view.setVisibility(View.INVISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 0.5 and then fade out
+ assertTrue(ViewUtils.getTransitionAlpha(view) <= 0.5f);
+ });
+ }
+
+ @Test
+ public void seekingFadeOut() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new Fade());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View[] viewArr = new View[1];
+
+ rule.runOnUiThread(() -> {
+ viewArr[0] = new View(activity);
+ mRoot.addView(viewArr[0], new ViewGroup.LayoutParams(100, 100));
+ });
+
+ final View view = viewArr[0];
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ view.setVisibility(View.GONE);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+
+ // Seek past the always there transition before the fade
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(1f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.LAYER_TYPE_HARDWARE, view.getLayerType());
+
+ // Seek to half through the fade
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(0.5f, ViewUtils.getTransitionAlpha(view), 0.01f);
+
+ // Seek past the fade
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+ assertEquals(1f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.GONE, view.getVisibility());
+
+ // Seek back to half through the fade
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(View.LAYER_TYPE_HARDWARE, view.getLayerType());
+ assertEquals(0.5f, ViewUtils.getTransitionAlpha(view), 0f);
+
+ // Seek before the fade:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(1f, ViewUtils.getTransitionAlpha(view), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+ assertEquals(View.LAYER_TYPE_NONE, view.getLayerType());
+
+ seekController.setCurrentPlayTimeMillis(450);
+ TransitionManager.beginDelayedTransition(mRoot, transition);
+ view.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from 0.5 and then fade in
+ assertTrue(ViewUtils.getTransitionAlpha(view) >= 0.5f);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingFadeInBeforeStart() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new Fade());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View view = new View(rule.getActivity());
+
+ // Animate it in
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ mRoot.addView(view, new ViewGroup.LayoutParams(100, 100));
+ });
+
+ rule.runOnUiThread(() -> {
+ // Starts off invisible
+ assertEquals(0f, view.getTransitionAlpha(), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+
+ // Fade all the way in
+ seekControllerArr[0].setCurrentPlayTimeMillis(600);
+ assertEquals(1f, view.getTransitionAlpha(), 0f);
+ assertEquals(View.VISIBLE, view.getVisibility());
+
+ // Fade out again
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ assertEquals(0f, view.getTransitionAlpha(), 0f);
+
+ // Animate to GONE
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Fade());
+ view.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It is already not shown, so it can just go straight to GONE
+ assertEquals(1f, view.getTransitionAlpha(), 0f);
+ assertEquals(View.GONE, view.getVisibility());
+ });
+ }
+
private void changeVisibility(final Fade fade, final ViewGroup container, final View target,
final int visibility) throws Throwable {
rule.runOnUiThread(new Runnable() {
diff --git a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSupportTest.java b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSupportTest.java
index 70e3f16..91efb1c 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSupportTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSupportTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -213,8 +213,8 @@
}
private Transition createTransition(int type) {
- final Transition.TransitionListener listener = mock(
- Transition.TransitionListener.class);
+ final Transition.TransitionListener listener = spy(
+ new TransitionListenerAdapter());
final AutoTransition transition = new AutoTransition();
transition.addListener(listener);
transition.setDuration(10);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/MultipleRootsTest.java b/transition/transition/src/androidTest/java/androidx/transition/MultipleRootsTest.java
index 131351b..57ea293 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/MultipleRootsTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/MultipleRootsTest.java
@@ -19,7 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -98,9 +98,9 @@
final ActivityScenario<TransitionActivity> scenario = prepareScenario(views);
final Transition.TransitionListener innerListener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Transition.TransitionListener outerListener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Fade innerTransition = new Fade();
innerTransition.setDuration(300);
@@ -143,9 +143,9 @@
final ActivityScenario<TransitionActivity> scenario = prepareScenario(views);
final Transition.TransitionListener innerListener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Transition.TransitionListener outerListener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Fade outerTransition = new Fade();
outerTransition.setDuration(300);
@@ -188,9 +188,9 @@
final ActivityScenario<TransitionActivity> scenario = prepareScenario(views);
final Transition.TransitionListener row1Listener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Transition.TransitionListener row2Listener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Fade row1Transition = new Fade();
row1Transition.setDuration(300);
@@ -234,7 +234,7 @@
// For Row 1, we run a subsequent transition at the end of the first transition.
final Transition.TransitionListener row1SecondListener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Fade row1SecondTransition = new Fade();
row1SecondTransition.setDuration(250);
row1SecondTransition.addListener(row1SecondListener);
@@ -253,7 +253,7 @@
// Only one transition for row 2.
final Transition.TransitionListener row2Listener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
final Slide row2Transition = new Slide();
row2Transition.setDuration(300);
row2Transition.addListener(row2Listener);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
new file mode 100644
index 0000000..36f41b2
--- /dev/null
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -0,0 +1,946 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.transition
+
+import android.graphics.Color
+import android.os.Build
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.LinearInterpolator
+import android.widget.LinearLayout
+import androidx.core.os.BuildCompat
+import androidx.core.util.Consumer
+import androidx.test.annotation.UiThreadTest
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.testutils.AnimationDurationScaleRule.Companion.createForAllTests
+import androidx.transition.Transition.TransitionListener
+import androidx.transition.test.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+@MediumTest
+class SeekTransitionTest : BaseTest() {
+ @get:Rule
+ val animationDurationScaleRule = createForAllTests(1f)
+
+ lateinit var view: View
+ lateinit var root: LinearLayout
+ lateinit var transition: Transition
+
+ @UiThreadTest
+ @Before
+ fun setUp() {
+ InstrumentationRegistry.getInstrumentation().setInTouchMode(false)
+ root = rule.activity.findViewById<View>(R.id.root) as LinearLayout
+ transition = Fade().also {
+ it.interpolator = LinearInterpolator()
+ }
+ view = View(root.context)
+ view.setBackgroundColor(Color.BLUE)
+ root.addView(view, ViewGroup.LayoutParams(100, 100))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ @UiThreadTest
+ fun onlySeekingTransitions() {
+ if (!BuildCompat.isAtLeastU()) throw IllegalArgumentException()
+ transition = object : Visibility() {}
+ TransitionManager.controlDelayedTransition(root, transition)
+ fail("Expected IllegalArgumentException")
+ }
+
+ @Test
+ fun waitForReady() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ @Suppress("UNCHECKED_CAST")
+ val readyCall: Consumer<TransitionSeekController> =
+ mock(Consumer::class.java) as Consumer<TransitionSeekController>
+
+ rule.runOnUiThread {
+ val controller = TransitionManager.controlDelayedTransition(root, transition)
+ assertThat(controller).isNotNull()
+ seekController = controller!!
+ assertThat(seekController.isReady).isFalse()
+ seekController.addOnReadyListener(readyCall)
+ view.visibility = View.GONE
+ }
+
+ verify(readyCall, timeout(3000)).accept(seekController)
+ assertThat(seekController.isReady).isTrue()
+ }
+
+ @Test
+ fun waitForReadyNoChange() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ @Suppress("UNCHECKED_CAST")
+ val readyCall: Consumer<TransitionSeekController> =
+ mock(Consumer::class.java) as Consumer<TransitionSeekController>
+
+ rule.runOnUiThread {
+ val controller = TransitionManager.controlDelayedTransition(root, transition)
+ assertThat(controller).isNotNull()
+ seekController = controller!!
+ assertThat(seekController.isReady).isFalse()
+ seekController.addOnReadyListener(readyCall)
+ view.requestLayout()
+ }
+
+ verify(readyCall, timeout(3000)).accept(seekController)
+ }
+
+ @Test
+ fun addListenerAfterReady() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ @Suppress("UNCHECKED_CAST")
+ val readyCall: Consumer<TransitionSeekController> =
+ mock(Consumer::class.java) as Consumer<TransitionSeekController>
+
+ rule.runOnUiThread {
+ val controller = TransitionManager.controlDelayedTransition(root, transition)
+ assertThat(controller).isNotNull()
+ seekController = controller!!
+ assertThat(seekController.isReady).isFalse()
+ seekController.addOnReadyListener(readyCall)
+ view.visibility = View.GONE
+ }
+
+ verify(readyCall, timeout(3000)).accept(seekController)
+
+ @Suppress("UNCHECKED_CAST")
+ val readyCall2: Consumer<TransitionSeekController> =
+ mock(Consumer::class.java) as Consumer<TransitionSeekController>
+
+ seekController.addOnReadyListener(readyCall2)
+ verify(readyCall, times(1)).accept(seekController)
+ }
+
+ @Test
+ fun seekTransition() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ rule.runOnUiThread {
+ val controller = TransitionManager.controlDelayedTransition(root, transition)
+ assertThat(controller).isNotNull()
+ seekController = controller!!
+ assertThat(seekController.isReady).isFalse()
+ view.visibility = View.GONE
+ }
+
+ verify(listener, timeout(1000)).onTransitionStart(any())
+ verify(listener, times(0)).onTransitionEnd(any())
+
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+
+ assertThat(seekController.durationMillis).isEqualTo(300)
+ assertThat(seekController.currentPlayTimeMillis).isEqualTo(0)
+
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+
+ seekController.currentPlayTimeMillis = 150
+ assertThat(view.transitionAlpha).isEqualTo(0.5f)
+ seekController.currentPlayTimeMillis = 299
+ assertThat(view.transitionAlpha).isWithin(0.001f).of(1f / 300f)
+ seekController.currentPlayTimeMillis = 300
+
+ verify(listener, times(1)).onTransitionEnd(any())
+
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun animationDoesNotTakeOverSeek() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val stateListener1 = spy(TransitionListenerAdapter())
+ transition.addListener(stateListener1)
+ rule.runOnUiThread {
+ val controller = TransitionManager.controlDelayedTransition(root, transition)
+ assertThat(controller).isNotNull()
+ seekController = controller!!
+ assertThat(seekController.isReady).isFalse()
+ view.visibility = View.GONE
+ }
+
+ verify(stateListener1, timeout(3000)).onTransitionStart(any())
+
+ val stateListener2 = spy(TransitionListenerAdapter())
+ val transition2 = Fade()
+ transition2.addListener(stateListener2)
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ TransitionManager.beginDelayedTransition(root, transition2)
+ view.visibility = View.GONE
+ }
+
+ verify(stateListener2, timeout(3000)).onTransitionStart(any())
+ verify(stateListener2, timeout(3000)).onTransitionEnd(any())
+ verify(stateListener1, times(0)).onTransitionEnd(any())
+ verify(stateListener1, times(0)).onTransitionCancel(any())
+
+ rule.runOnUiThread {
+ // Seek is still controlling the visibility
+ assertThat(view.transitionAlpha).isEqualTo(0.5f)
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+
+ seekController.currentPlayTimeMillis = 300
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun seekCannotTakeOverAnimation() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val stateListener1 = spy(TransitionListenerAdapter())
+ transition.addListener(stateListener1)
+ transition.duration = 1000
+ transition.addListener(TransitionListenerAdapter())
+ rule.runOnUiThread {
+ TransitionManager.beginDelayedTransition(root, transition)
+ view.visibility = View.GONE
+ }
+
+ verify(stateListener1, timeout(3000)).onTransitionStart(any())
+
+ val stateListener2 = spy(TransitionListenerAdapter())
+ val transition2 = Fade()
+ transition2.duration = 3000
+ transition2.addListener(stateListener2)
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition2)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ // The second transition doesn't have any animations in it because it didn't take over.
+ assertThat(seekController.isReady).isTrue()
+ assertThat(seekController.durationMillis).isEqualTo(0)
+ }
+
+ // The first animation should continue
+ verify(stateListener1, timeout(3000)).onTransitionEnd(any())
+
+ // The animation is ended
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun seekCannotTakeOverSeek() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController1: TransitionSeekController
+
+ val stateListener1 = spy(TransitionListenerAdapter())
+ transition.addListener(stateListener1)
+ transition.duration = 3000
+ rule.runOnUiThread {
+ seekController1 = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ assertThat(seekController1.isReady).isTrue()
+ }
+
+ val stateListener2 = spy(TransitionListenerAdapter())
+ val transition2 = Fade()
+ transition2.duration = 3000
+ transition2.addListener(stateListener2)
+
+ rule.runOnUiThread {
+ seekController1.currentPlayTimeMillis = 1500
+ // First transition should be started now
+ verify(stateListener1, times(1)).onTransitionStart(any())
+ TransitionManager.controlDelayedTransition(root, transition2)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ // second transition should just start/end immediately
+ verify(stateListener2, times(1)).onTransitionStart(any())
+ verify(stateListener2, times(1)).onTransitionEnd(any())
+
+ // first transition should still be controllable
+ verify(stateListener1, times(0)).onTransitionEnd(any())
+
+ // second transition should be ready and taking over the previous animation
+ assertThat(view.transitionAlpha).isEqualTo(0.5f)
+ seekController1.currentPlayTimeMillis = 3000
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun seekReplacesSeek() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController1: TransitionSeekController
+
+ val stateListener1 = spy(TransitionListenerAdapter())
+ transition.addListener(stateListener1)
+ transition.duration = 3000
+ rule.runOnUiThread {
+ seekController1 = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ verify(stateListener1, timeout(3000)).onTransitionStart(any())
+
+ val stateListener2 = spy(TransitionListenerAdapter())
+ val transition2 = Fade()
+ transition2.duration = 3000
+ transition2.addListener(stateListener2)
+
+ lateinit var seekController2: TransitionSeekController
+ rule.runOnUiThread {
+ seekController1.currentPlayTimeMillis = 1500
+ seekController2 = TransitionManager.controlDelayedTransition(root, transition2)!!
+ view.visibility = View.VISIBLE
+ }
+
+ verify(stateListener2, timeout(3000)).onTransitionStart(any())
+ rule.runOnUiThread {}
+ verify(stateListener1, times(1)).onTransitionEnd(any())
+ assertThat(seekController2.isReady).isTrue()
+
+ rule.runOnUiThread {
+ verify(stateListener2, never()).onTransitionEnd(any())
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(0.5f)
+ seekController2.currentPlayTimeMillis = 3000
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ verify(stateListener2, times(1)).onTransitionEnd(any())
+ }
+ }
+
+ @Test
+ fun animateToEnd() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ seekController.animateToEnd()
+ }
+
+ verify(listener, timeout(3000)).onTransitionEnd(any())
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ val runningTransitions = TransitionManager.getRunningTransitions()
+ assertThat(runningTransitions[root]).isEmpty()
+ }
+ }
+
+ @Test
+ fun animateToStart() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ seekController.animateToStart()
+ }
+
+ verify(listener, timeout(3000)).onTransitionEnd(any())
+ val listener2 = spy(TransitionListenerAdapter())
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+
+ // Now set it back to the original state with a fast transition
+ transition.removeListener(listener)
+ transition.addListener(listener2)
+ transition.duration = 0
+ TransitionManager.beginDelayedTransition(root, transition)
+ view.visibility = View.VISIBLE
+ root.invalidate()
+ }
+ verify(listener2, timeout(3000)).onTransitionStart(any())
+ rule.runOnUiThread {
+ verify(listener2, times(1)).onTransitionEnd(any())
+ // All transitions should be ended
+ val runningTransitions = TransitionManager.getRunningTransitions()
+ assertThat(runningTransitions[root]).isEmpty()
+ }
+ }
+
+ @Test
+ fun animateToStartAfterAnimateToEnd() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ seekController.animateToEnd()
+ }
+
+ rule.runOnUiThread {
+ seekController.animateToStart()
+ }
+
+ verify(listener, timeout(3000)).onTransitionEnd(any())
+
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun animateToEndAfterAnimateToStart() {
+ if (!BuildCompat.isAtLeastU()) return
+ lateinit var seekController: TransitionSeekController
+
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ seekController.animateToStart()
+ }
+
+ rule.runOnUiThread {
+ seekController.animateToEnd()
+ }
+
+ verify(listener, timeout(3000)).onTransitionEnd(any())
+
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ }
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun seekAfterAnimate() {
+ if (!BuildCompat.isAtLeastU()) throw IllegalStateException("Not supported before U")
+ lateinit var seekController: TransitionSeekController
+ transition.duration = 5000
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 150
+ seekController.animateToEnd()
+ }
+
+ rule.runOnUiThread {
+ seekController.currentPlayTimeMillis = 120
+ }
+ }
+
+ @Test
+ fun seekTransitionSet() {
+ if (!BuildCompat.isAtLeastU()) return
+ transition = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ }
+ val view2 = View(root.context)
+ view2.setBackgroundColor(Color.GREEN)
+
+ val view3 = View(root.context)
+ view3.setBackgroundColor(Color.RED)
+
+ rule.runOnUiThread {
+ root.addView(view2, ViewGroup.LayoutParams(100, 100))
+ root.addView(view3, ViewGroup.LayoutParams(100, 100))
+ view2.visibility = View.GONE
+ }
+
+ lateinit var seekController: TransitionSeekController
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view2.visibility = View.VISIBLE
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ assertThat(seekController.durationMillis).isEqualTo(600)
+ assertThat(seekController.currentPlayTimeMillis).isEqualTo(0)
+ seekController.currentPlayTimeMillis = 0
+
+ // We should be at the start
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(0f)
+
+ // seek to the end of the fade out
+ seekController.currentPlayTimeMillis = 300
+
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(0f)
+
+ // seek to the end of transition
+ seekController.currentPlayTimeMillis = 600
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(1f)
+
+ // seek back to the middle
+ seekController.currentPlayTimeMillis = 300
+
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(0f)
+
+ // back to the beginning
+ seekController.currentPlayTimeMillis = 0
+
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(1f)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(0f)
+ }
+ }
+
+ @Test
+ fun animateToEndTransitionSet() {
+ if (!BuildCompat.isAtLeastU()) return
+ transition = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ }
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ val view2 = View(root.context)
+ view2.setBackgroundColor(Color.GREEN)
+
+ val view3 = View(root.context)
+ view3.setBackgroundColor(Color.RED)
+
+ rule.runOnUiThread {
+ root.addView(view2, ViewGroup.LayoutParams(100, 100))
+ root.addView(view3, ViewGroup.LayoutParams(100, 100))
+ view2.visibility = View.GONE
+ }
+
+ lateinit var seekController: TransitionSeekController
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view2.visibility = View.VISIBLE
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ verify(listener, times(0)).onTransitionEnd(any())
+ // seek to the end of the fade out
+ seekController.currentPlayTimeMillis = 300
+ verify(listener, times(0)).onTransitionEnd(any())
+
+ seekController.animateToEnd()
+ }
+ verify(listener, timeout(3000)).onTransitionEnd(any())
+
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.GONE)
+ assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view2.transitionAlpha).isEqualTo(1f)
+ val runningTransitions = TransitionManager.getRunningTransitions()
+ assertThat(runningTransitions[root]).isEmpty()
+ }
+ }
+
+ @Test
+ fun animateToStartTransitionSet() {
+ if (!BuildCompat.isAtLeastU()) return
+ transition = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ }
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ val view2 = View(root.context)
+ view2.setBackgroundColor(Color.GREEN)
+
+ val view3 = View(root.context)
+ view3.setBackgroundColor(Color.RED)
+
+ rule.runOnUiThread {
+ root.addView(view2, ViewGroup.LayoutParams(100, 100))
+ root.addView(view3, ViewGroup.LayoutParams(100, 100))
+ view2.visibility = View.GONE
+ }
+
+ lateinit var seekController: TransitionSeekController
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view2.visibility = View.VISIBLE
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ // seek to near the end of the fade out
+ seekController.currentPlayTimeMillis = 299
+
+ seekController.animateToStart()
+ }
+ verify(listener, timeout(3000)).onTransitionEnd(any(), eq(true))
+ verify(listener, never()).onTransitionEnd(any(), eq(false))
+ verify(listener, times(1)).onTransitionEnd(any(), eq(true))
+
+ val transition2 = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ it.duration = 0
+ }
+
+ val listener2 = spy(TransitionListenerAdapter())
+ transition2.addListener(listener2)
+ rule.runOnUiThread {
+ TransitionManager.beginDelayedTransition(root, transition2)
+ view.visibility = View.VISIBLE
+ view2.visibility = View.GONE
+ }
+ verify(listener2, timeout(3000)).onTransitionStart(any(), eq(false))
+
+ rule.runOnUiThread {
+ verify(listener, times(1)).onTransitionCancel(any())
+ verify(listener, times(1)).onTransitionEnd(any(), eq(false))
+ verify(listener2, times(1)).onTransitionEnd(any())
+ val runningTransitions = TransitionManager.getRunningTransitions()
+ assertThat(runningTransitions[root]).isEmpty()
+ }
+ }
+
+ @Test
+ fun cancelPartOfTransitionSet() {
+ if (!BuildCompat.isAtLeastU()) return
+ transition = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ }
+ val listener = spy(TransitionListenerAdapter())
+ transition.addListener(listener)
+
+ val view2 = View(root.context)
+ view2.setBackgroundColor(Color.GREEN)
+
+ val view3 = View(root.context)
+ view3.setBackgroundColor(Color.RED)
+
+ rule.runOnUiThread {
+ root.addView(view2, ViewGroup.LayoutParams(100, 100))
+ root.addView(view3, ViewGroup.LayoutParams(100, 100))
+ view2.visibility = View.GONE
+ }
+
+ lateinit var seekController: TransitionSeekController
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view2.visibility = View.VISIBLE
+ view.visibility = View.GONE
+ }
+
+ val transition2 = TransitionSet().also {
+ it.addTransition(Fade(Fade.MODE_OUT))
+ .addTransition(Fade(Fade.MODE_IN))
+ .ordering = TransitionSet.ORDERING_SEQUENTIAL
+ }
+
+ val listener2 = spy(TransitionListenerAdapter())
+ transition2.addListener(listener2)
+
+ rule.runOnUiThread {
+ // seek to the end of the fade out
+ seekController.currentPlayTimeMillis = 300
+ TransitionManager.beginDelayedTransition(root, transition2)
+ // Undo making the view visible
+ view2.visibility = View.GONE
+ }
+ verify(listener2, timeout(3000)).onTransitionStart(any())
+ verify(listener2, timeout(3000)).onTransitionEnd(any())
+
+ // The first transition shouldn't end. You can still control it.
+ verify(listener, times(0)).onTransitionEnd(any())
+
+ rule.runOnUiThread {
+ // view2 should now be gone
+ assertThat(view2.visibility).isEqualTo(View.GONE)
+ assertThat(view2.transitionAlpha).isEqualTo(1f)
+
+ // Try to seek further. It should not affect view2 because that transition should be
+ // canceled.
+ seekController.currentPlayTimeMillis = 600
+ assertThat(view2.visibility).isEqualTo(View.GONE)
+ assertThat(view2.transitionAlpha).isEqualTo(1f)
+
+ verify(listener, times(1)).onTransitionEnd(any())
+ }
+ }
+
+ @Test
+ fun onTransitionCallsForwardAndReversed() {
+ if (!BuildCompat.isAtLeastU()) return
+ val listener = spy(TransitionListenerAdapter())
+ transition = Fade()
+ transition.addListener(listener)
+
+ lateinit var seekController: TransitionSeekController
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+ rule.runOnUiThread {
+ verifyCallCounts(listener, startForward = 1)
+ seekController.currentPlayTimeMillis = 300
+ verifyCallCounts(listener, startForward = 1, endForward = 1)
+ seekController.currentPlayTimeMillis = 150
+ verifyCallCounts(listener, startForward = 1, endForward = 1, startReverse = 1)
+ seekController.currentPlayTimeMillis = 0
+ verifyCallCounts(
+ listener,
+ startForward = 1,
+ endForward = 1,
+ startReverse = 1,
+ endReverse = 1
+ )
+ }
+ }
+
+ @Test
+ fun onTransitionCallsForwardAndReversedTransitionSet() {
+ if (!BuildCompat.isAtLeastU()) return
+ val fadeOut = Fade(Fade.MODE_OUT)
+ val outListener = spy(TransitionListenerAdapter())
+ fadeOut.addListener(outListener)
+ val fadeIn = Fade(Fade.MODE_IN)
+ val inListener = spy(TransitionListenerAdapter())
+ fadeIn.addListener(inListener)
+ val set = TransitionSet()
+ set.addTransition(fadeOut)
+ set.addTransition(fadeIn)
+ set.setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
+ val setListener = spy(TransitionListenerAdapter())
+ set.addListener(setListener)
+
+ val view2 = View(view.context)
+
+ lateinit var seekController: TransitionSeekController
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, set)!!
+ view.visibility = View.GONE
+ root.addView(view2, ViewGroup.LayoutParams(100, 100))
+ }
+
+ rule.runOnUiThread {
+ verifyCallCounts(setListener, startForward = 1)
+ verifyCallCounts(outListener, startForward = 1)
+ verifyCallCounts(inListener)
+ seekController.currentPlayTimeMillis = 301
+ verifyCallCounts(setListener, startForward = 1)
+ verifyCallCounts(outListener, startForward = 1, endForward = 1)
+ verifyCallCounts(inListener, startForward = 1)
+ seekController.currentPlayTimeMillis = 600
+ verifyCallCounts(setListener, startForward = 1, endForward = 1)
+ verifyCallCounts(outListener, startForward = 1, endForward = 1)
+ verifyCallCounts(inListener, startForward = 1, endForward = 1)
+ seekController.currentPlayTimeMillis = 301
+ verifyCallCounts(setListener, startForward = 1, endForward = 1, startReverse = 1)
+ verifyCallCounts(outListener, startForward = 1, endForward = 1)
+ verifyCallCounts(inListener, startForward = 1, endForward = 1, startReverse = 1)
+ seekController.currentPlayTimeMillis = 299
+ verifyCallCounts(setListener, startForward = 1, endForward = 1, startReverse = 1)
+ verifyCallCounts(outListener, startForward = 1, endForward = 1, startReverse = 1)
+ verifyCallCounts(
+ inListener,
+ startForward = 1,
+ endForward = 1,
+ startReverse = 1,
+ endReverse = 1
+ )
+ seekController.currentPlayTimeMillis = 0
+ verifyCallCounts(
+ setListener,
+ startForward = 1,
+ endForward = 1,
+ startReverse = 1,
+ endReverse = 1
+ )
+ verifyCallCounts(
+ outListener,
+ startForward = 1,
+ endForward = 1,
+ startReverse = 1,
+ endReverse = 1
+ )
+ verifyCallCounts(
+ inListener,
+ startForward = 1,
+ endForward = 1,
+ startReverse = 1,
+ endReverse = 1
+ )
+ }
+ }
+
+ private fun verifyCallCounts(
+ listener: TransitionListener,
+ startForward: Int = 0,
+ endForward: Int = 0,
+ startReverse: Int = 0,
+ endReverse: Int = 0
+ ) {
+ verify(listener, times(startForward)).onTransitionStart(any(), eq(false))
+ verify(listener, times(endForward)).onTransitionEnd(any(), eq(false))
+ verify(listener, times(startReverse)).onTransitionStart(any(), eq(true))
+ verify(listener, times(endReverse)).onTransitionEnd(any(), eq(true))
+ }
+
+ @Test
+ fun pauseResumeOnSeek() {
+ if (!BuildCompat.isAtLeastU()) return
+ var pauseCount = 0
+ var resumeCount = 0
+ var setPauseCount = 0
+ var setResumeCount = 0
+ val set = TransitionSet().also {
+ it.addTransition(Fade().apply {
+ addListener(object : TransitionListenerAdapter() {
+ override fun onTransitionPause(transition: Transition) {
+ pauseCount++
+ }
+
+ override fun onTransitionResume(transition: Transition) {
+ resumeCount++
+ }
+ })
+ })
+ it.addListener(object : TransitionListenerAdapter() {
+ override fun onTransitionPause(transition: Transition) {
+ setPauseCount++
+ }
+
+ override fun onTransitionResume(transition: Transition) {
+ setResumeCount++
+ }
+ })
+ }
+ transition = TransitionSet().also {
+ it.addTransition(AlwaysTransition("before"))
+ it.addTransition(set)
+ it.setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
+ }
+
+ lateinit var seekController: TransitionSeekController
+ lateinit var view: View
+
+ rule.runOnUiThread {
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view = View(rule.activity)
+ root.addView(view, ViewGroup.LayoutParams(100, 100))
+ }
+
+ rule.runOnUiThread {
+ assertThat(view.visibility).isEqualTo(View.VISIBLE)
+ assertThat(view.transitionAlpha).isEqualTo(0f)
+
+ // Move it to the end and then back to the beginning:
+ seekController.currentPlayTimeMillis = 600
+ seekController.currentPlayTimeMillis = 0
+
+ seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+ view.visibility = View.GONE
+ }
+
+ rule.runOnUiThread {
+ assertThat(pauseCount).isEqualTo(1)
+ assertThat(resumeCount).isEqualTo(1)
+ assertThat(setPauseCount).isEqualTo(1)
+ assertThat(setResumeCount).isEqualTo(1)
+ }
+ }
+}
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SlideEdgeTest.java b/transition/transition/src/androidTest/java/androidx/transition/SlideEdgeTest.java
index 7858f8c..e0af8b2 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SlideEdgeTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/SlideEdgeTest.java
@@ -19,23 +19,29 @@
import static androidx.transition.AtLeastOnceWithin.atLeastOnceWithin;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalMatchers.and;
import static org.mockito.AdditionalMatchers.eq;
import static org.mockito.AdditionalMatchers.gt;
import static org.mockito.AdditionalMatchers.lt;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.graphics.Color;
+import android.os.Build;
import android.view.Gravity;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import androidx.core.os.BuildCompat;
import androidx.core.util.Pair;
import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -81,7 +87,7 @@
int slideEdge = SLIDE_EDGES.get(i).first;
final Slide slide = new Slide(slideEdge);
final Transition.TransitionListener listener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
slide.addListener(listener);
final View redSquare = spy(new View(rule.getActivity()));
@@ -149,7 +155,7 @@
int slideEdge = pair.first;
final Slide slide = new Slide(slideEdge);
final Transition.TransitionListener listener =
- mock(Transition.TransitionListener.class);
+ spy(new TransitionListenerAdapter());
slide.addListener(listener);
@@ -212,6 +218,249 @@
}
}
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingSlideOut() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ transition.addTransition(new Slide());
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View redSquare = new View(activity);
+ redSquare.setBackgroundColor(Color.RED);
+ rule.runOnUiThread(() -> {
+ mRoot.addView(redSquare, new ViewGroup.LayoutParams(100, 100));
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ redSquare.setVisibility(View.GONE);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ float[] translationValues = new float[1];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, ViewUtils.getTransitionAlpha(redSquare), 0f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+
+ // Seek past the always there transition before the slide
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+
+ // Seek half way:
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(0f, redSquare.getTranslationX(), 0.01f);
+ assertNotEquals(0f, redSquare.getTranslationY(), 0.01f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ translationValues[0] = redSquare.getTranslationY();
+
+ // Seek past the end
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+ assertEquals(View.GONE, redSquare.getVisibility());
+
+ // Seek before the slide:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+
+ seekController.setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and decrease
+ assertEquals(0, redSquare.getTranslationX(), 0.01f);
+ assertEquals(translationValues[0], redSquare.getTranslationY(), 1f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekingSlideIn() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+
+ TransitionSet transition = new TransitionSet();
+ transition.addTransition(new AlwaysTransition("before"));
+ Slide slide = new Slide();
+ slide.setInterpolator(new LinearInterpolator());
+ transition.addTransition(slide);
+ transition.addTransition(new AlwaysTransition("after"));
+ transition.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+
+ View redSquare = new View(activity);
+ redSquare.setBackgroundColor(Color.RED);
+ rule.runOnUiThread(() -> {
+ mRoot.addView(redSquare, new ViewGroup.LayoutParams(100, 100));
+ redSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, transition);
+ redSquare.setVisibility(View.VISIBLE);
+ });
+
+ final TransitionSeekController seekController = seekControllerArr[0];
+
+ float[] translationValues = new float[1];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, ViewUtils.getTransitionAlpha(redSquare), 0f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0.01f);
+ assertTrue(redSquare.getTranslationY() >= mRoot.getHeight());
+
+ float startY = redSquare.getTranslationY();
+
+ // Seek past the always there transition before the slide
+ seekController.setCurrentPlayTimeMillis(300);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(startY, redSquare.getTranslationY(), 0f);
+
+ // Seek half way:
+ seekController.setCurrentPlayTimeMillis(450);
+ assertEquals(0f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(startY / 2f, redSquare.getTranslationY(), 0.01f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ translationValues[0] = redSquare.getTranslationY();
+
+ // Seek past the end
+ seekController.setCurrentPlayTimeMillis(800);
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+
+ // Seek before the slide:
+ seekController.setCurrentPlayTimeMillis(250);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(0, redSquare.getTranslationX(), 0f);
+ assertEquals(startY, redSquare.getTranslationY(), 0f);
+
+ seekController.setCurrentPlayTimeMillis(450);
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and increase
+ assertEquals(0f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(translationValues[0], redSquare.getTranslationY(), 1f);
+
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(View.GONE, redSquare.getVisibility());
+ assertEquals(0f, redSquare.getTranslationX(), 0f);
+ assertEquals(0f, redSquare.getTranslationY(), 0f);
+ });
+ }
+
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Test
+ public void seekWithTranslation() throws Throwable {
+ if (!BuildCompat.isAtLeastU()) {
+ return; // only supported on U+
+ }
+ final TransitionActivity activity = rule.getActivity();
+ TransitionSeekController[] seekControllerArr = new TransitionSeekController[1];
+ View redSquare = new View(activity);
+ redSquare.setBackgroundColor(Color.RED);
+ rule.runOnUiThread(() -> {
+ mRoot.addView(redSquare, new ViewGroup.LayoutParams(100, 100));
+ redSquare.setTranslationX(1f);
+ redSquare.setTranslationY(5f);
+ });
+
+ rule.runOnUiThread(() -> {
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.GONE);
+ });
+
+ final float[] interruptedTranslation = new float[1];
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, redSquare.getTranslationY(), 0.01f);
+
+
+ seekControllerArr[0].setCurrentPlayTimeMillis(150);
+ interruptedTranslation[0] = redSquare.getTranslationY();
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should start from half way values and increase
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(interruptedTranslation[0], redSquare.getTranslationY(), 1f);
+
+ // make sure it would go to the start value
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, redSquare.getTranslationY(), 0.01f);
+
+ // Now go back to the interrupted position again:
+ seekControllerArr[0].setCurrentPlayTimeMillis(0);
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(interruptedTranslation[0], redSquare.getTranslationY(), 1f);
+
+ // Send it back to GONE
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(interruptedTranslation[0], redSquare.getTranslationY(), 1f);
+
+ // it should move away (toward the top-left)
+ seekControllerArr[0].setCurrentPlayTimeMillis(299);
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertTrue(redSquare.getTranslationY() > interruptedTranslation[0]);
+
+ seekControllerArr[0] = TransitionManager.controlDelayedTransition(mRoot, new Slide());
+ redSquare.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ // It should end up at the initial translation
+ seekControllerArr[0].setCurrentPlayTimeMillis(300);
+ assertEquals(1f, redSquare.getTranslationX(), 0.01f);
+ assertEquals(5f, redSquare.getTranslationY(), 0.01f);
+ });
+ }
+
private void verifyNoTranslation(View view) {
assertEquals(0f, view.getTranslationX(), 0.01f);
assertEquals(0f, view.getTranslationY(), 0.01f);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java b/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
index 002c7d4..86f7240 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/TransitionActivity.java
@@ -41,6 +41,7 @@
overridePendingTransition(0, 0);
}
+ @SuppressWarnings("deprecation")
@Override
public void finish() {
super.finish();
diff --git a/transition/transition/src/androidTest/java/androidx/transition/TransitionManagerTest.java b/transition/transition/src/androidTest/java/androidx/transition/TransitionManagerTest.java
index 3d4397e..c80073a 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/TransitionManagerTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/TransitionManagerTest.java
@@ -21,8 +21,8 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -181,7 +181,7 @@
final Transition transition = new AutoTransition();
// This transition is very long, but will be forced to end as soon as it starts
transition.setDuration(30000);
- final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listener = spy(new TransitionListenerAdapter());
transition.addListener(listener);
rule.runOnUiThread(new Runnable() {
@Override
@@ -204,7 +204,7 @@
final ViewGroup root = rule.getActivity().getRoot();
final Transition transition = new AutoTransition();
transition.setDuration(0);
- final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ final Transition.TransitionListener listener = spy(new TransitionListenerAdapter());
transition.addListener(listener);
rule.runOnUiThread(new Runnable() {
@Override
diff --git a/transition/transition/src/androidTest/java/androidx/transition/TransitionTest.java b/transition/transition/src/androidTest/java/androidx/transition/TransitionTest.java
index 4c586a5..30a88f5 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/TransitionTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/TransitionTest.java
@@ -35,6 +35,7 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
@@ -377,6 +378,61 @@
assertThat(transition.isTransitionRequired(start, end), is(true));
}
+ // Any listener that is added by the transition itself should not be in the global set of
+ // listeners. They should be limited to the executing transition.
+ @Test
+ public void internalListenersNotGlobal() throws Throwable {
+ rule.runOnUiThread(() -> {
+ mScenes[0].enter();
+ });
+ View view = rule.getActivity().findViewById(R.id.view0);
+
+ int[] startCount = new int[1];
+ Transition transition = new Visibility() {
+ private Animator createAnimator() {
+ addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+ startCount[0]++;
+ }
+ });
+ return ValueAnimator.ofFloat(0f, 100f);
+ }
+
+ @Nullable
+ @Override
+ public Animator onDisappear(@NonNull ViewGroup sceneRoot, @NonNull View view,
+ @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+ return createAnimator();
+ }
+
+ @Nullable
+ @Override
+ public Animator onAppear(@NonNull ViewGroup sceneRoot, @NonNull View view,
+ @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
+ return createAnimator();
+ }
+ };
+
+ rule.runOnUiThread(() -> {
+ ViewGroup root = rule.getActivity().getRoot();
+ TransitionManager.beginDelayedTransition(root, transition);
+ view.setVisibility(View.GONE);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(1, startCount[0]);
+
+ ViewGroup root = rule.getActivity().getRoot();
+ TransitionManager.beginDelayedTransition(root, transition);
+ view.setVisibility(View.VISIBLE);
+ });
+
+ rule.runOnUiThread(() -> {
+ assertEquals(2, startCount[0]);
+ });
+ }
+
private void showInitialScene() throws Throwable {
SyncRunnable enter0 = new SyncRunnable();
mScenes[0].setEnterAction(enter0);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/TranslationAnimationCreatorTest.java b/transition/transition/src/androidTest/java/androidx/transition/TranslationAnimationCreatorTest.java
index 976277a..207880f 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/TranslationAnimationCreatorTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/TranslationAnimationCreatorTest.java
@@ -53,7 +53,7 @@
assertEquals(20, view.getTranslationX(), 0.01);
verify(transition).addListener(listenerCaptor.capture());
- listenerCaptor.getValue().onTransitionEnd(transition);
+ listenerCaptor.getValue().onTransitionEnd(transition, false);
// but onTransitionEnd does
assertEquals(0, view.getTranslationX(), 0.01);
}
diff --git a/transition/transition/src/androidTest/java/androidx/transition/VisibilityTest.java b/transition/transition/src/androidTest/java/androidx/transition/VisibilityTest.java
index c433a98..73ce42f 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/VisibilityTest.java
+++ b/transition/transition/src/androidTest/java/androidx/transition/VisibilityTest.java
@@ -23,7 +23,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -124,7 +124,7 @@
return ValueAnimator.ofFloat(0, 1);
}
});
- Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ Transition.TransitionListener listener = spy(new TransitionListenerAdapter());
set.addListener(listener);
// remove view
@@ -177,7 +177,7 @@
return ValueAnimator.ofFloat(0, 1);
}
};
- Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ Transition.TransitionListener listener = spy(new TransitionListenerAdapter());
visibility.addListener(listener);
// remove view
diff --git a/transition/transition/src/main/java/androidx/transition/ChangeBounds.java b/transition/transition/src/main/java/androidx/transition/ChangeBounds.java
index a0fcc2c..e72333d 100644
--- a/transition/transition/src/main/java/androidx/transition/ChangeBounds.java
+++ b/transition/transition/src/main/java/androidx/transition/ChangeBounds.java
@@ -20,18 +20,13 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
@@ -66,24 +61,6 @@
PROPNAME_WINDOW_Y
};
- private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY =
- new Property<Drawable, PointF>(PointF.class, "boundsOrigin") {
- private Rect mBounds = new Rect();
-
- @Override
- public void set(Drawable object, PointF value) {
- object.copyBounds(mBounds);
- mBounds.offsetTo(Math.round(value.x), Math.round(value.y));
- object.setBounds(mBounds);
- }
-
- @Override
- public PointF get(Drawable object) {
- object.copyBounds(mBounds);
- return new PointF(mBounds.left, mBounds.top);
- }
- };
-
private static final Property<ViewBounds, PointF> TOP_LEFT_PROPERTY =
new Property<ViewBounds, PointF>(PointF.class, "topLeft") {
@Override
@@ -161,11 +138,9 @@
}
};
- private int[] mTempLocation = new int[2];
private boolean mResizeClip = false;
- private boolean mReparent = false;
- private static RectEvaluator sRectEvaluator = new RectEvaluator();
+ private static final RectEvaluator sRectEvaluator = new RectEvaluator();
public ChangeBounds() {
}
@@ -182,6 +157,11 @@
setResizeClip(resizeClip);
}
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
@NonNull
@Override
public String[] getTransitionProperties() {
@@ -223,11 +203,6 @@
values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom()));
values.values.put(PROPNAME_PARENT, values.view.getParent());
- if (mReparent) {
- values.view.getLocationInWindow(mTempLocation);
- values.values.put(PROPNAME_WINDOW_X, mTempLocation[0]);
- values.values.put(PROPNAME_WINDOW_Y, mTempLocation[1]);
- }
if (mResizeClip) {
values.values.put(PROPNAME_CLIP, ViewCompat.getClipBounds(view));
}
@@ -237,6 +212,13 @@
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
captureValues(transitionValues);
+ if (mResizeClip) {
+ Rect clipSize =
+ (Rect) transitionValues.view.getTag(R.id.transition_clip);
+ if (clipSize != null) {
+ transitionValues.values.put(PROPNAME_CLIP, clipSize);
+ }
+ }
}
@Override
@@ -244,19 +226,6 @@
captureValues(transitionValues);
}
- private boolean parentMatches(View startParent, View endParent) {
- boolean parentMatches = true;
- if (mReparent) {
- TransitionValues endValues = getMatchedTransitionValues(startParent, true);
- if (endValues == null) {
- parentMatches = startParent == endParent;
- } else {
- parentMatches = endParent == endValues.view;
- }
- }
- return parentMatches;
- }
-
@Override
@Nullable
public Animator createAnimator(@NonNull final ViewGroup sceneRoot,
@@ -272,188 +241,118 @@
return null;
}
final View view = endValues.view;
- if (parentMatches(startParent, endParent)) {
- Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
- Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final int startLeft = startBounds.left;
- final int endLeft = endBounds.left;
- final int startTop = startBounds.top;
- final int endTop = endBounds.top;
- final int startRight = startBounds.right;
- final int endRight = endBounds.right;
- final int startBottom = startBounds.bottom;
- final int endBottom = endBounds.bottom;
- final int startWidth = startRight - startLeft;
- final int startHeight = startBottom - startTop;
- final int endWidth = endRight - endLeft;
- final int endHeight = endBottom - endTop;
- Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP);
- Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP);
- int numChanges = 0;
- if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) {
- if (startLeft != endLeft || startTop != endTop) ++numChanges;
- if (startRight != endRight || startBottom != endBottom) ++numChanges;
- }
- if ((startClip != null && !startClip.equals(endClip))
- || (startClip == null && endClip != null)) {
- ++numChanges;
- }
- if (numChanges > 0) {
- Animator anim;
- if (!mResizeClip) {
- ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startRight,
- startBottom);
- if (numChanges == 2) {
- if (startWidth == endWidth && startHeight == endHeight) {
- Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
- endTop);
- anim = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
- topLeftPath);
- } else {
- final ViewBounds viewBounds = new ViewBounds(view);
- Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
- endLeft, endTop);
- ObjectAnimator topLeftAnimator = ObjectAnimatorUtils
- .ofPointF(viewBounds, TOP_LEFT_PROPERTY, topLeftPath);
-
- Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
- endRight, endBottom);
- ObjectAnimator bottomRightAnimator = ObjectAnimatorUtils.ofPointF(
- viewBounds, BOTTOM_RIGHT_PROPERTY, bottomRightPath);
- AnimatorSet set = new AnimatorSet();
- set.playTogether(topLeftAnimator, bottomRightAnimator);
- anim = set;
- set.addListener(new AnimatorListenerAdapter() {
- // We need a strong reference to viewBounds until the
- // animator ends (The ObjectAnimator holds only a weak reference).
- @SuppressWarnings("unused")
- private ViewBounds mViewBounds = viewBounds;
- });
- }
- } else if (startLeft != endLeft || startTop != endTop) {
- Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
- endLeft, endTop);
- anim = ObjectAnimatorUtils.ofPointF(view, TOP_LEFT_ONLY_PROPERTY,
- topLeftPath);
- } else {
- Path bottomRight = getPathMotion().getPath(startRight, startBottom,
- endRight, endBottom);
- anim = ObjectAnimatorUtils.ofPointF(view, BOTTOM_RIGHT_ONLY_PROPERTY,
- bottomRight);
- }
- } else {
- int maxWidth = Math.max(startWidth, endWidth);
- int maxHeight = Math.max(startHeight, endHeight);
-
- ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startLeft + maxWidth,
- startTop + maxHeight);
-
- ObjectAnimator positionAnimator = null;
- if (startLeft != endLeft || startTop != endTop) {
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final int startLeft = startBounds.left;
+ final int endLeft = endBounds.left;
+ final int startTop = startBounds.top;
+ final int endTop = endBounds.top;
+ final int startRight = startBounds.right;
+ final int endRight = endBounds.right;
+ final int startBottom = startBounds.bottom;
+ final int endBottom = endBounds.bottom;
+ final int startWidth = startRight - startLeft;
+ final int startHeight = startBottom - startTop;
+ final int endWidth = endRight - endLeft;
+ final int endHeight = endBottom - endTop;
+ Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP);
+ Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+ int numChanges = 0;
+ if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) {
+ if (startLeft != endLeft || startTop != endTop) ++numChanges;
+ if (startRight != endRight || startBottom != endBottom) ++numChanges;
+ }
+ if ((startClip != null && !startClip.equals(endClip))
+ || (startClip == null && endClip != null)) {
+ ++numChanges;
+ }
+ if (numChanges > 0) {
+ Animator anim;
+ if (!mResizeClip) {
+ ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startRight,
+ startBottom);
+ if (numChanges == 2) {
+ if (startWidth == endWidth && startHeight == endHeight) {
Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
endTop);
- positionAnimator = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
+ anim = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
topLeftPath);
- }
- final Rect finalClip = endClip;
- if (startClip == null) {
- startClip = new Rect(0, 0, startWidth, startHeight);
- }
- if (endClip == null) {
- endClip = new Rect(0, 0, endWidth, endHeight);
- }
- ObjectAnimator clipAnimator = null;
- if (!startClip.equals(endClip)) {
- ViewCompat.setClipBounds(view, startClip);
- clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
- startClip, endClip);
- clipAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mIsCanceled;
+ } else {
+ final ViewBounds viewBounds = new ViewBounds(view);
+ Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+ endLeft, endTop);
+ ObjectAnimator topLeftAnimator = ObjectAnimatorUtils
+ .ofPointF(viewBounds, TOP_LEFT_PROPERTY, topLeftPath);
- @Override
- public void onAnimationCancel(Animator animation) {
- mIsCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mIsCanceled) {
- ViewCompat.setClipBounds(view, finalClip);
- ViewUtils.setLeftTopRightBottom(view, endLeft, endTop, endRight,
- endBottom);
- }
- }
+ Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
+ endRight, endBottom);
+ ObjectAnimator bottomRightAnimator = ObjectAnimatorUtils.ofPointF(
+ viewBounds, BOTTOM_RIGHT_PROPERTY, bottomRightPath);
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(topLeftAnimator, bottomRightAnimator);
+ anim = set;
+ set.addListener(new AnimatorListenerAdapter() {
+ // We need a strong reference to viewBounds until the
+ // animator ends (The ObjectAnimator holds only a weak reference).
+ @SuppressWarnings("unused")
+ private final ViewBounds mViewBounds = viewBounds;
});
}
- anim = TransitionUtils.mergeAnimators(positionAnimator,
- clipAnimator);
+ } else if (startLeft != endLeft || startTop != endTop) {
+ Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+ endLeft, endTop);
+ anim = ObjectAnimatorUtils.ofPointF(view, TOP_LEFT_ONLY_PROPERTY,
+ topLeftPath);
+ } else {
+ Path bottomRight = getPathMotion().getPath(startRight, startBottom,
+ endRight, endBottom);
+ anim = ObjectAnimatorUtils.ofPointF(view, BOTTOM_RIGHT_ONLY_PROPERTY,
+ bottomRight);
}
- if (view.getParent() instanceof ViewGroup) {
- final ViewGroup parent = (ViewGroup) view.getParent();
- ViewGroupUtils.suppressLayout(parent, true);
- TransitionListener transitionListener = new TransitionListenerAdapter() {
- boolean mCanceled = false;
+ } else {
+ int maxWidth = Math.max(startWidth, endWidth);
+ int maxHeight = Math.max(startHeight, endHeight);
- @Override
- public void onTransitionCancel(@NonNull Transition transition) {
- ViewGroupUtils.suppressLayout(parent, false);
- mCanceled = true;
- }
+ ViewUtils.setLeftTopRightBottom(view, startLeft, startTop, startLeft + maxWidth,
+ startTop + maxHeight);
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- if (!mCanceled) {
- ViewGroupUtils.suppressLayout(parent, false);
- }
- transition.removeListener(this);
- }
-
- @Override
- public void onTransitionPause(@NonNull Transition transition) {
- ViewGroupUtils.suppressLayout(parent, false);
- }
-
- @Override
- public void onTransitionResume(@NonNull Transition transition) {
- ViewGroupUtils.suppressLayout(parent, true);
- }
- };
- addListener(transitionListener);
+ ObjectAnimator positionAnimator = null;
+ if (startLeft != endLeft || startTop != endTop) {
+ Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
+ endTop);
+ positionAnimator = ObjectAnimatorUtils.ofPointF(view, POSITION_PROPERTY,
+ topLeftPath);
}
- return anim;
+ boolean startClipIsNull = startClip == null;
+ if (startClipIsNull) {
+ startClip = new Rect(0, 0, startWidth, startHeight);
+ }
+ boolean endClipIsNull = endClip == null;
+ if (endClipIsNull) {
+ endClip = new Rect(0, 0, endWidth, endHeight);
+ }
+ ObjectAnimator clipAnimator = null;
+ if (!startClip.equals(endClip)) {
+ ViewCompat.setClipBounds(view, startClip);
+ clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
+ startClip, endClip);
+ ClipListener listener = new ClipListener(view,
+ startClip, startClipIsNull, endClip, endClipIsNull,
+ startLeft, startTop, startRight, startBottom,
+ endLeft, endTop, endRight, endBottom
+ );
+ clipAnimator.addListener(listener);
+ addListener(listener);
+ }
+ anim = TransitionUtils.mergeAnimators(positionAnimator,
+ clipAnimator);
}
- } else {
- int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
- int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
- int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
- int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
- // TODO: also handle size changes: check bounds and animate size changes
- if (startX != endX || startY != endY) {
- sceneRoot.getLocationInWindow(mTempLocation);
- Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- view.draw(canvas);
- @SuppressWarnings("deprecation") final BitmapDrawable drawable = new BitmapDrawable(
- bitmap);
- final float transitionAlpha = ViewUtils.getTransitionAlpha(view);
- ViewUtils.setTransitionAlpha(view, 0);
- ViewUtils.getOverlay(sceneRoot).add(drawable);
- Path topLeftPath = getPathMotion().getPath(startX - mTempLocation[0],
- startY - mTempLocation[1], endX - mTempLocation[0],
- endY - mTempLocation[1]);
- PropertyValuesHolder origin = PropertyValuesHolderUtils.ofPointF(
- DRAWABLE_ORIGIN_PROPERTY, topLeftPath);
- ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- ViewUtils.getOverlay(sceneRoot).remove(drawable);
- ViewUtils.setTransitionAlpha(view, transitionAlpha);
- }
- });
- return anim;
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ ViewGroupUtils.suppressLayout(parent, true);
+ getRootTransition().addListener(new SuppressLayoutListener(parent));
}
+ return anim;
}
return null;
}
@@ -464,7 +363,7 @@
private int mTop;
private int mRight;
private int mBottom;
- private View mView;
+ private final View mView;
private int mTopLeftCalls;
private int mBottomRightCalls;
@@ -495,7 +394,149 @@
mTopLeftCalls = 0;
mBottomRightCalls = 0;
}
-
}
+ private static class ClipListener
+ extends AnimatorListenerAdapter implements TransitionListener {
+ private final View mView;
+ private final Rect mStartClip;
+ private final boolean mStartClipIsNull;
+ private final Rect mEndClip;
+ private final boolean mEndClipIsNull;
+ private final int mStartLeft, mStartTop, mStartRight, mStartBottom;
+ private final int mEndLeft, mEndTop, mEndRight, mEndBottom;
+
+ private boolean mIsCanceled;
+
+ ClipListener(View view,
+ Rect startClip,
+ boolean startClipIsNull,
+ Rect endClip,
+ boolean endClipIsNull,
+ int startLeft,
+ int startTop,
+ int startRight,
+ int startBottom,
+ int endLeft,
+ int endTop,
+ int endRight,
+ int endBottom
+ ) {
+ mView = view;
+ mStartClip = startClip;
+ mStartClipIsNull = startClipIsNull;
+ mEndClip = endClip;
+ mEndClipIsNull = endClipIsNull;
+ mStartLeft = startLeft;
+ mStartTop = startTop;
+ mStartRight = startRight;
+ mStartBottom = startBottom;
+ mEndLeft = endLeft;
+ mEndTop = endTop;
+ mEndRight = endRight;
+ mEndBottom = endBottom;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ onAnimationStart(animation, false);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEnd(animation, false);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation, boolean isReverse) {
+ int maxWidth = Math.max(mStartRight - mStartLeft, mEndRight - mEndLeft);
+ int maxHeight = Math.max(mStartBottom - mStartTop, mEndBottom - mEndTop);
+
+ int left = isReverse ? mEndLeft : mStartLeft;
+ int top = isReverse ? mEndTop : mStartTop;
+ ViewUtils.setLeftTopRightBottom(mView, left, top, left + maxWidth, top + maxHeight);
+
+ Rect clip = isReverse ? mEndClip : mStartClip;
+ ViewCompat.setClipBounds(mView, clip);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation, boolean isReverse) {
+ if (mIsCanceled) {
+ return;
+ }
+ Rect clip = isReverse
+ ? (mStartClipIsNull ? null : mStartClip)
+ : (mEndClipIsNull ? null : mEndClip);
+ ViewCompat.setClipBounds(mView, clip);
+ if (isReverse) {
+ ViewUtils.setLeftTopRightBottom(mView, mStartLeft, mStartTop, mStartRight,
+ mStartBottom);
+ } else {
+ ViewUtils.setLeftTopRightBottom(mView, mEndLeft, mEndTop, mEndRight, mEndBottom);
+ }
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ mIsCanceled = true;
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ Rect pauseClip = ViewCompat.getClipBounds(mView);
+ mView.setTag(R.id.transition_clip, pauseClip);
+ Rect clip = mEndClipIsNull ? null : mEndClip;
+ ViewCompat.setClipBounds(mView, clip);
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ Rect pauseClip = (Rect) mView.getTag(R.id.transition_clip);
+ mView.setTag(R.id.transition_clip, null);
+ ViewCompat.setClipBounds(mView, pauseClip);
+ }
+
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ }
+ }
+
+ private static class SuppressLayoutListener extends TransitionListenerAdapter {
+ boolean mCanceled = false;
+
+ final ViewGroup mParent;
+
+ SuppressLayoutListener(@NonNull ViewGroup parent) {
+ mParent = parent;
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ ViewGroupUtils.suppressLayout(mParent, false);
+ mCanceled = true;
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ if (!mCanceled) {
+ ViewGroupUtils.suppressLayout(mParent, false);
+ }
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ ViewGroupUtils.suppressLayout(mParent, false);
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ ViewGroupUtils.suppressLayout(mParent, true);
+ }
+ }
}
diff --git a/transition/transition/src/main/java/androidx/transition/ChangeClipBounds.java b/transition/transition/src/main/java/androidx/transition/ChangeClipBounds.java
index b568ae2..bcd40a3 100644
--- a/transition/transition/src/main/java/androidx/transition/ChangeClipBounds.java
+++ b/transition/transition/src/main/java/androidx/transition/ChangeClipBounds.java
@@ -44,6 +44,10 @@
PROPNAME_CLIP,
};
+ // Represents a null Rect in the tag. If null were used instead, we would treat it
+ // as not set.
+ static final Rect NULL_SENTINEL = new Rect();
+
@Override
@NonNull
public String[] getTransitionProperties() {
@@ -57,13 +61,28 @@
super(context, attrs);
}
- private void captureValues(TransitionValues values) {
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
+ @SuppressWarnings("ReferenceEquality") // Reference comparison with NULL_SENTINEL
+ private void captureValues(TransitionValues values, boolean clipFromTag) {
View view = values.view;
if (view.getVisibility() == View.GONE) {
return;
}
- Rect clip = ViewCompat.getClipBounds(view);
+ Rect clip = null;
+ if (clipFromTag) {
+ clip = (Rect) view.getTag(R.id.transition_clip);
+ }
+ if (clip == null) {
+ clip = ViewCompat.getClipBounds(view);
+ }
+ if (clip == NULL_SENTINEL) {
+ clip = null;
+ }
values.values.put(PROPNAME_CLIP, clip);
if (clip == null) {
Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
@@ -73,12 +92,12 @@
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, true);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, false);
}
@Nullable
@@ -93,33 +112,83 @@
}
Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
- final boolean endIsNull = end == null;
if (start == null && end == null) {
return null; // No animation required since there is no clip.
}
- if (start == null) {
- start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
- } else if (end == null) {
- end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- }
- if (start.equals(end)) {
+ Rect startClip = start == null ? (Rect) startValues.values.get(PROPNAME_BOUNDS) : start;
+ Rect endClip = end == null ? (Rect) endValues.values.get(PROPNAME_BOUNDS) : end;
+
+ if (startClip.equals(endClip)) {
return null;
}
ViewCompat.setClipBounds(endValues.view, start);
RectEvaluator evaluator = new RectEvaluator(new Rect());
ObjectAnimator animator = ObjectAnimator.ofObject(endValues.view, ViewUtils.CLIP_BOUNDS,
- evaluator, start, end);
- if (endIsNull) {
- final View endView = endValues.view;
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- ViewCompat.setClipBounds(endView, null);
- }
- });
- }
+ evaluator, startClip, endClip);
+ View view = endValues.view;
+ Listener listener = new Listener(view, start, end);
+ animator.addListener(listener);
+ addListener(listener);
return animator;
}
+
+ private static class Listener extends AnimatorListenerAdapter implements TransitionListener {
+ private final Rect mStart;
+ private final Rect mEnd;
+ private final View mView;
+
+ Listener(View view, Rect start, Rect end) {
+ mView = view;
+ mStart = start;
+ mEnd = end;
+ }
+
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ Rect clipBounds = ViewCompat.getClipBounds(mView);
+ if (clipBounds == null) {
+ clipBounds = NULL_SENTINEL;
+ }
+ mView.setTag(R.id.transition_clip, clipBounds);
+ ViewCompat.setClipBounds(mView, mEnd);
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ Rect clipBounds = (Rect) mView.getTag(R.id.transition_clip);
+ ViewCompat.setClipBounds(mView, clipBounds);
+ mView.setTag(R.id.transition_clip, null);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEnd(animation, false);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation, boolean isReverse) {
+ if (!isReverse) {
+ ViewCompat.setClipBounds(mView, mEnd);
+ } else {
+ ViewCompat.setClipBounds(mView, mStart);
+ }
+ }
+ }
}
diff --git a/transition/transition/src/main/java/androidx/transition/ChangeImageTransform.java b/transition/transition/src/main/java/androidx/transition/ChangeImageTransform.java
index cfdbbd3..1d6b6d0 100644
--- a/transition/transition/src/main/java/androidx/transition/ChangeImageTransform.java
+++ b/transition/transition/src/main/java/androidx/transition/ChangeImageTransform.java
@@ -17,6 +17,7 @@
package androidx.transition;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.content.Context;
@@ -79,7 +80,12 @@
super(context, attrs);
}
- private void captureValues(TransitionValues transitionValues) {
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
+ private void captureValues(TransitionValues transitionValues, boolean useIntermediate) {
View view = transitionValues.view;
if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
return;
@@ -98,17 +104,24 @@
Rect bounds = new Rect(left, top, right, bottom);
values.put(PROPNAME_BOUNDS, bounds);
- values.put(PROPNAME_MATRIX, copyImageMatrix(imageView));
+ Matrix matrix = null;
+ if (useIntermediate) {
+ matrix = (Matrix) imageView.getTag(R.id.transition_image_transform);
+ }
+ if (matrix == null) {
+ matrix = copyImageMatrix(imageView);
+ }
+ values.put(PROPNAME_MATRIX, matrix);
}
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, true);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, false);
}
@Override
@@ -168,6 +181,10 @@
}
ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
+ Listener listener = new Listener(imageView, startMatrix, endMatrix);
+ animator.addListener(listener);
+ AnimatorUtils.addPauseListener(animator, listener);
+ addListener(listener);
}
return animator;
@@ -241,4 +258,85 @@
return matrix;
}
+ private static class Listener extends AnimatorListenerAdapter implements TransitionListener,
+ AnimatorUtils.AnimatorPauseListenerCompat {
+ private final ImageView mImageView;
+ private final Matrix mStartMatrix;
+ private final Matrix mEndMatrix;
+ private boolean mIsBeforeAnimator = true;
+
+ Listener(ImageView imageView, Matrix startMatrix, Matrix endMatrix) {
+ mImageView = imageView;
+ mStartMatrix = startMatrix;
+ mEndMatrix = endMatrix;
+ }
+
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ if (mIsBeforeAnimator) {
+ saveMatrix(mStartMatrix);
+ }
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ restoreMatrix();
+ }
+
+ @Override
+ public void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
+ mIsBeforeAnimator = false;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsBeforeAnimator = false;
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
+ mIsBeforeAnimator = isReverse;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsBeforeAnimator = false;
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ Matrix pauseMatrix = (Matrix) ((ObjectAnimator) animation).getAnimatedValue();
+ saveMatrix(pauseMatrix);
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ restoreMatrix();
+ }
+
+ private void restoreMatrix() {
+ Matrix pauseMatrix = (Matrix) mImageView.getTag(R.id.transition_image_transform);
+ if (pauseMatrix != null) {
+ ImageViewUtils.animateTransform(mImageView, pauseMatrix);
+ mImageView.setTag(R.id.transition_image_transform, null);
+ }
+ }
+
+ private void saveMatrix(Matrix pauseMatrix) {
+ mImageView.setTag(R.id.transition_image_transform, pauseMatrix);
+ ImageViewUtils.animateTransform(mImageView, mEndMatrix);
+ }
+ }
}
diff --git a/transition/transition/src/main/java/androidx/transition/ChangeScroll.java b/transition/transition/src/main/java/androidx/transition/ChangeScroll.java
index d2b9a21..03fa492 100644
--- a/transition/transition/src/main/java/androidx/transition/ChangeScroll.java
+++ b/transition/transition/src/main/java/androidx/transition/ChangeScroll.java
@@ -57,6 +57,11 @@
captureValues(transitionValues);
}
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
@Nullable
@Override
public String[] getTransitionProperties() {
diff --git a/transition/transition/src/main/java/androidx/transition/ChangeTransform.java b/transition/transition/src/main/java/androidx/transition/ChangeTransform.java
index 84b869f..0d0cd09 100644
--- a/transition/transition/src/main/java/androidx/transition/ChangeTransform.java
+++ b/transition/transition/src/main/java/androidx/transition/ChangeTransform.java
@@ -326,48 +326,8 @@
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix,
valuesProperty, translationProperty);
- final Matrix finalEndMatrix = endMatrix;
-
- AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
- private boolean mIsCanceled;
- private Matrix mTempMatrix = new Matrix();
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mIsCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mIsCanceled) {
- if (handleParentChange && mUseOverlay) {
- setCurrentMatrix(finalEndMatrix);
- } else {
- view.setTag(R.id.transition_transform, null);
- view.setTag(R.id.parent_matrix, null);
- }
- }
- ViewUtils.setAnimationMatrix(view, null);
- transforms.restore(view);
- }
-
- @Override
- public void onAnimationPause(Animator animation) {
- Matrix currentMatrix = pathAnimatorMatrix.getMatrix();
- setCurrentMatrix(currentMatrix);
- }
-
- @Override
- public void onAnimationResume(Animator animation) {
- setIdentityTransforms(view);
- }
-
- private void setCurrentMatrix(Matrix currentMatrix) {
- mTempMatrix.set(currentMatrix);
- view.setTag(R.id.transition_transform, mTempMatrix);
- transforms.restore(view);
- }
- };
+ Listener listener = new Listener(view, transforms, pathAnimatorMatrix, endMatrix,
+ handleParentChange, mUseOverlay);
animator.addListener(listener);
AnimatorUtils.addPauseListener(animator, listener);
@@ -591,4 +551,61 @@
}
}
+ private static class Listener extends AnimatorListenerAdapter implements
+ AnimatorUtils.AnimatorPauseListenerCompat {
+ private boolean mIsCanceled;
+ private final Matrix mTempMatrix = new Matrix();
+ private final boolean mHandleParentChange;
+ private final boolean mUseOverlay;
+ private final View mView;
+ private final Transforms mTransforms;
+ private final PathAnimatorMatrix mPathAnimatorMatrix;
+ private final Matrix mEndMatrix;
+
+ Listener(View view, Transforms transforms, PathAnimatorMatrix pathAnimatorMatrix,
+ Matrix endMatrix, boolean handleParentChange, boolean useOverlay) {
+ mHandleParentChange = handleParentChange;
+ mUseOverlay = useOverlay;
+ mView = view;
+ mTransforms = transforms;
+ mPathAnimatorMatrix = pathAnimatorMatrix;
+ mEndMatrix = endMatrix;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mIsCanceled) {
+ if (mHandleParentChange && mUseOverlay) {
+ setCurrentMatrix(mEndMatrix);
+ } else {
+ mView.setTag(R.id.transition_transform, null);
+ mView.setTag(R.id.parent_matrix, null);
+ }
+ }
+ ViewUtils.setAnimationMatrix(mView, null);
+ mTransforms.restore(mView);
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ Matrix currentMatrix = mPathAnimatorMatrix.getMatrix();
+ setCurrentMatrix(currentMatrix);
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ setIdentityTransforms(mView);
+ }
+
+ private void setCurrentMatrix(Matrix currentMatrix) {
+ mTempMatrix.set(currentMatrix);
+ mView.setTag(R.id.transition_transform, mTempMatrix);
+ mTransforms.restore(mView);
+ }
+ }
}
diff --git a/transition/transition/src/main/java/androidx/transition/Explode.java b/transition/transition/src/main/java/androidx/transition/Explode.java
index ea53bc2..a671288 100644
--- a/transition/transition/src/main/java/androidx/transition/Explode.java
+++ b/transition/transition/src/main/java/androidx/transition/Explode.java
@@ -79,6 +79,11 @@
captureValues(transitionValues);
}
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
@Nullable
@Override
public Animator onAppear(@NonNull ViewGroup sceneRoot, @NonNull View view,
diff --git a/transition/transition/src/main/java/androidx/transition/Fade.java b/transition/transition/src/main/java/androidx/transition/Fade.java
index ac1f499..a264655 100644
--- a/transition/transition/src/main/java/androidx/transition/Fade.java
+++ b/transition/transition/src/main/java/androidx/transition/Fade.java
@@ -118,6 +118,11 @@
ViewUtils.getTransitionAlpha(transitionValues.view));
}
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
/**
* Utility method to handle creating and running the Animator.
*/
@@ -133,14 +138,6 @@
}
FadeAnimatorListener listener = new FadeAnimatorListener(view);
anim.addListener(listener);
- addListener(new TransitionListenerAdapter() {
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- ViewUtils.setTransitionAlpha(view, 1);
- ViewUtils.clearNonTransitionAlpha(view);
- transition.removeListener(this);
- }
- });
return anim;
}
@@ -153,6 +150,7 @@
Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = "
+ startView + ", " + view);
}
+ ViewUtils.saveNonTransitionAlpha(view);
float startAlpha = getStartAlpha(startValues, 0);
if (startAlpha == 1) {
startAlpha = 0;
@@ -166,7 +164,11 @@
@Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
ViewUtils.saveNonTransitionAlpha(view);
float startAlpha = getStartAlpha(startValues, 1);
- return createAnimation(view, startAlpha, 0);
+ Animator animator = createAnimation(view, startAlpha, 0);
+ if (animator == null) {
+ ViewUtils.setTransitionAlpha(view, getStartAlpha(endValues, 1f));
+ }
+ return animator;
}
private static float getStartAlpha(TransitionValues startValues, float fallbackValue) {
@@ -201,11 +203,22 @@
@Override
public void onAnimationEnd(Animator animation) {
ViewUtils.setTransitionAlpha(mView, 1);
+ ViewUtils.clearNonTransitionAlpha(mView);
if (mLayerTypeChanged) {
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
+ @Override
+ public void onAnimationEnd(Animator animation, boolean isReverse) {
+ if (!isReverse) {
+ ViewUtils.setTransitionAlpha(mView, 1);
+ ViewUtils.clearNonTransitionAlpha(mView);
+ }
+ if (mLayerTypeChanged) {
+ mView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
}
}
diff --git a/transition/transition/src/main/java/androidx/transition/Slide.java b/transition/transition/src/main/java/androidx/transition/Slide.java
index ae54f98..62d2dcf 100644
--- a/transition/transition/src/main/java/androidx/transition/Slide.java
+++ b/transition/transition/src/main/java/androidx/transition/Slide.java
@@ -194,6 +194,11 @@
captureValues(transitionValues);
}
+ @Override
+ public boolean isSeekingSupported() {
+ return true;
+ }
+
/**
* Change the edge that Views appear and disappear from.
*
diff --git a/transition/transition/src/main/java/androidx/transition/Transition.java b/transition/transition/src/main/java/androidx/transition/Transition.java
index fc1a168..8c32506 100644
--- a/transition/transition/src/main/java/androidx/transition/Transition.java
+++ b/transition/transition/src/main/java/androidx/transition/Transition.java
@@ -20,13 +20,16 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -40,14 +43,19 @@
import android.widget.ListView;
import android.widget.Spinner;
+import androidx.annotation.DoNotInline;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.collection.ArrayMap;
import androidx.collection.LongSparseArray;
import androidx.core.content.res.TypedArrayUtils;
+import androidx.core.os.BuildCompat;
+import androidx.core.util.Consumer;
import androidx.core.view.ViewCompat;
import java.lang.annotation.Retention;
@@ -200,6 +208,7 @@
private int[] mMatchOrder = DEFAULT_MATCH_ORDER;
private ArrayList<TransitionValues> mStartValuesList; // only valid after playTransition starts
private ArrayList<TransitionValues> mEndValuesList; // only valid after playTransitions starts
+ private TransitionListener[] mListenersCache;
// Per-animator information used for later canceling when future transitions overlap
private static ThreadLocal<ArrayMap<Animator, Transition.AnimationInfo>> sRunningAnimators =
@@ -220,21 +229,21 @@
// Number of per-target instances of this Transition currently running. This count is
// determined by calls to start() and end()
- private int mNumInstances = 0;
+ int mNumInstances = 0;
// Whether this transition is currently paused, due to a call to pause()
private boolean mPaused = false;
// Whether this transition has ended. Used to avoid pause/resume on transitions
// that have completed
- private boolean mEnded = false;
+ boolean mEnded = false;
// The set of listeners to be sent transition lifecycle events.
private ArrayList<Transition.TransitionListener> mListeners = null;
// The set of animators collected from calls to createAnimator(),
// to be run in runAnimators()
- private ArrayList<Animator> mAnimators = new ArrayList<>();
+ ArrayList<Animator> mAnimators = new ArrayList<>();
// The function for calculating the Animation start delay.
TransitionPropagation mPropagation;
@@ -251,6 +260,18 @@
// for adding curves to x/y View motion.
private PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
+ // The total duration of this Transition, in milliseconds. This is used only if
+ // TransitionManager.controlDelayedTransition() is called to begin a seekable Transition.
+ long mTotalDuration;
+
+ // The SeekController created in TransitionManager.controlDelayedTransition() on the
+ // root TransitionSet.
+ SeekController mSeekController;
+
+ // For Transitions in a TransitionSet that are played sequentially, this is the offset
+ // (in milliseconds) from the start of the containing TransitionSet of this Transition
+ long mSeekOffsetInParent;
+
/**
* Constructs a Transition object with no target objects. A transition with
* no targets defaults to running on all target objects in the scene hierarchy
@@ -328,6 +349,19 @@
}
/**
+ * If this Transition is not part of a TransitionSet, this is returned. If it is part
+ * of a TransitionSet, the parent TransitionSets are walked until a TransitionSet is found
+ * that isn't contained in another TransitionSet.
+ */
+ @NonNull
+ public final Transition getRootTransition() {
+ if (mParent != null) {
+ return mParent.getRootTransition();
+ }
+ return this;
+ }
+
+ /**
* Sets the duration of this transition. By default, there is no duration
* (indicated by a negative number), which means that the Animator created by
* the transition will have its own specified duration. If the duration of a
@@ -487,6 +521,20 @@
}
/**
+ * Creates and returns a new TransitionSeekController, tied it to this Transition.
+ * This should only be called once on the cloned transition for controlling the
+ * Transition's progress. The Transition will begin without starting any of the
+ * animations.
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ @NonNull
+ TransitionSeekController createSeekController() {
+ mSeekController = new SeekController();
+ addListener(mSeekController);
+ return mSeekController;
+ }
+
+ /**
* Sets the order in which Transition matches View start and end values.
* <p>
* The default behavior is to match first by {@link android.view.View#getTransitionName()},
@@ -664,6 +712,7 @@
ArrayMap<View, TransitionValues> unmatchedStart = new ArrayMap<>(startValues.mViewValues);
ArrayMap<View, TransitionValues> unmatchedEnd = new ArrayMap<>(endValues.mViewValues);
+ //noinspection ForLoopReplaceableByForEach
for (int i = 0; i < mMatchOrder.length; i++) {
switch (mMatchOrder[i]) {
case MATCH_INSTANCE:
@@ -695,6 +744,7 @@
* TransitionSet subclass overrides this method and delegates it to
* each of its children in succession.
*/
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
void createAnimators(@NonNull ViewGroup sceneRoot, @NonNull TransitionValuesMaps startValues,
@NonNull TransitionValuesMaps endValues,
@NonNull ArrayList<TransitionValues> startValuesList,
@@ -706,6 +756,7 @@
long minStartDelay = Long.MAX_VALUE;
SparseIntArray startDelays = new SparseIntArray();
int startValuesListCount = startValuesList.size();
+ boolean hasSeekController = getRootTransition().mSeekController != null;
for (int i = 0; i < startValuesListCount; ++i) {
TransitionValues start = startValuesList.get(i);
TransitionValues end = endValuesList.get(i);
@@ -780,7 +831,12 @@
minStartDelay = Math.min(delay, minStartDelay);
}
AnimationInfo info = new AnimationInfo(view, getName(), this,
- ViewUtils.getWindowId(sceneRoot), infoValues);
+ ViewUtils.getWindowId(sceneRoot), infoValues, animator);
+ if (hasSeekController) {
+ AnimatorSet set = new AnimatorSet();
+ set.play(animator);
+ animator = set;
+ }
runningAnimators.put(animator, info);
mAnimators.add(animator);
}
@@ -791,8 +847,10 @@
for (int i = 0; i < startDelays.size(); i++) {
int index = startDelays.keyAt(i);
Animator animator = mAnimators.get(index);
- long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay();
- animator.setStartDelay(delay);
+ AnimationInfo info = runningAnimators.get(animator);
+ long delay = startDelays.valueAt(i) - minStartDelay
+ + info.mAnimator.getStartDelay();
+ info.mAnimator.setStartDelay(delay);
}
}
}
@@ -801,7 +859,7 @@
* Internal utility method for checking whether a given view/id
* is valid for this transition, where "valid" means that either
* the Transition has no target/targetId list (the default, in which
- * cause the transition should act on all views in the hiearchy), or
+ * cause the transition should act on all views in the hierarchy), or
* the given view is in the target list or the view id is in the
* targetId list. If the target parameter is null, then the target list
* is not checked (this is in the case of ListView items, where the
@@ -861,7 +919,7 @@
/**
* This is called internally once all animations have been set up by the
- * transition hierarchy. \
+ * transition hierarchy.
*
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -906,6 +964,42 @@
}
/**
+ * Configures the animators to be ready for animation.
+ *
+ * The animators' start delay, duration, and interpolator are set based on the Transition's
+ * values. The duration is calculated. It also adds the animators to mCurrentAnimators so that
+ * each animator can support seeking.
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ void prepareAnimatorsForSeeking() {
+ ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+ // Now prepare every Animator that was previously created for this transition
+ mTotalDuration = 0;
+ for (int i = 0; i < mAnimators.size(); i++) {
+ Animator anim = mAnimators.get(i);
+ if (DBG) {
+ Log.d(LOG_TAG, " anim: " + anim);
+ }
+ AnimationInfo info = runningAnimators.get(anim);
+ if (anim != null && info != null) {
+ if (getDuration() >= 0) {
+ info.mAnimator.setDuration(getDuration());
+ }
+ if (getStartDelay() >= 0) {
+ info.mAnimator.setStartDelay(
+ getStartDelay() + info.mAnimator.getStartDelay());
+ }
+ if (getInterpolator() != null) {
+ info.mAnimator.setInterpolator(getInterpolator());
+ }
+ mCurrentAnimators.add(anim);
+ mTotalDuration = Math.max(mTotalDuration, Impl26.getTotalDuration(anim));
+ }
+ }
+ mAnimators.clear();
+ }
+
+ /**
* Captures the values in the start scene for the properties that this
* transition monitors. These values are then passed as the startValues
* structure in a later call to
@@ -1454,6 +1548,20 @@
}
/**
+ * Returns {@code true} if the Transition can be used by
+ * {@link TransitionManager#controlDelayedTransition(ViewGroup, Transition)}. This means
+ * that any the state must be ready before any {@link Animator} returned by
+ * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)} has started and
+ * if the Animator has ended, it must be able to restore the state when starting in reverse.
+ * If a Transition must know when the entire transition has ended, a {@link TransitionListener}
+ * can be added to {@link #getRootTransition()} and it can listen for
+ * {@link TransitionListener#onTransitionEnd(Transition)}.
+ */
+ public boolean isSeekingSupported() {
+ return false;
+ }
+
+ /**
* Recursive method that captures values for the given view and the
* hierarchy underneath it.
*
@@ -1715,14 +1823,7 @@
Animator animator = mCurrentAnimators.get(i);
AnimatorUtils.pause(animator);
}
- if (mListeners != null && mListeners.size() > 0) {
- @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
- (ArrayList<TransitionListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onTransitionPause(this);
- }
- }
+ notifyListeners(TransitionNotification.ON_PAUSE, false);
mPaused = true;
}
}
@@ -1742,14 +1843,7 @@
Animator animator = mCurrentAnimators.get(i);
AnimatorUtils.resume(animator);
}
- if (mListeners != null && mListeners.size() > 0) {
- @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
- (ArrayList<TransitionListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onTransitionResume(this);
- }
- }
+ notifyListeners(TransitionNotification.ON_RESUME, false);
}
mPaused = false;
}
@@ -1760,6 +1854,7 @@
* createAnimators() to set things up and create all of the animations and then
* runAnimations() to actually start the animations.
*/
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
void playTransition(@NonNull ViewGroup sceneRoot) {
mStartValuesList = new ArrayList<>();
mEndValuesList = new ArrayList<>();
@@ -1784,7 +1879,22 @@
boolean cancel = (startValues != null || endValues != null)
&& oldInfo.mTransition.isTransitionRequired(oldValues, endValues);
if (cancel) {
- if (anim.isRunning() || anim.isStarted()) {
+ Transition transition = oldInfo.mTransition;
+ if (transition.getRootTransition().mSeekController != null) {
+ // Seeking, so cancel the transition directly rather than going through
+ // a listener
+ anim.cancel();
+ transition.mCurrentAnimators.remove(anim);
+ runningAnimators.remove(anim);
+ if (transition.mCurrentAnimators.size() == 0) {
+ transition.notifyListeners(TransitionNotification.ON_CANCEL, false);
+ if (!transition.mEnded) {
+ transition.mEnded = true;
+ transition.notifyListeners(TransitionNotification.ON_END,
+ false);
+ }
+ }
+ } else if (anim.isRunning() || anim.isStarted()) {
if (DBG) {
Log.d(LOG_TAG, "Canceling anim " + anim);
}
@@ -1801,7 +1911,13 @@
}
createAnimators(sceneRoot, mStartValues, mEndValues, mStartValuesList, mEndValuesList);
- runAnimators();
+ if (mSeekController == null) {
+ runAnimators();
+ } else if (BuildCompat.isAtLeastU()) {
+ prepareAnimatorsForSeeking();
+ mSeekController.setCurrentPlayTimeMillis(0);
+ mSeekController.ready();
+ }
}
/**
@@ -1909,14 +2025,7 @@
@RestrictTo(LIBRARY_GROUP_PREFIX)
protected void start() {
if (mNumInstances == 0) {
- if (mListeners != null && mListeners.size() > 0) {
- @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
- (ArrayList<TransitionListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onTransitionStart(this);
- }
- }
+ notifyListeners(TransitionNotification.ON_START, false);
mEnded = false;
}
mNumInstances++;
@@ -1936,14 +2045,7 @@
protected void end() {
--mNumInstances;
if (mNumInstances == 0) {
- if (mListeners != null && mListeners.size() > 0) {
- @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
- (ArrayList<TransitionListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onTransitionEnd(this);
- }
- }
+ notifyListeners(TransitionNotification.ON_END, false);
for (int i = 0; i < mStartValues.mItemIdValues.size(); ++i) {
View view = mStartValues.mItemIdValues.valueAt(i);
if (view != null) {
@@ -1996,14 +2098,7 @@
Animator animator = mCurrentAnimators.get(i);
animator.cancel();
}
- if (mListeners != null && mListeners.size() > 0) {
- @SuppressWarnings("unchecked") ArrayList<TransitionListener> tmpListeners =
- (ArrayList<TransitionListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onTransitionCancel(this);
- }
- }
+ notifyListeners(TransitionNotification.ON_CANCEL, false);
}
/**
@@ -2169,6 +2264,7 @@
return;
}
boolean containsAll = true;
+ //noinspection ForLoopReplaceableByForEach
for (int i = 0; i < propertyNames.length; i++) {
if (!transitionValues.values.containsKey(propertyNames[i])) {
containsAll = false;
@@ -2201,6 +2297,10 @@
clone.mEndValues = new TransitionValuesMaps();
clone.mStartValuesList = null;
clone.mEndValuesList = null;
+ clone.mSeekController = null;
+ if (mListeners != null) {
+ clone.mListeners = new ArrayList<>(mListeners);
+ }
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
@@ -2224,39 +2324,114 @@
return mName;
}
+ /**
+ * Calls notification on each listener.
+ */
+ void notifyListeners(TransitionNotification notification, boolean isReversed) {
+ if (mListeners != null && !mListeners.isEmpty()) {
+ // Use a cache so that we don't have to keep allocating on every notification
+ int size = mListeners.size();
+ TransitionListener[] listeners = mListenersCache == null
+ ? new TransitionListener[size] : mListenersCache;
+ mListenersCache = null;
+ listeners = mListeners.toArray(listeners);
+ for (int i = 0; i < size; i++) {
+ notification.notifyListener(listeners[i], Transition.this, isReversed);
+ listeners[i] = null;
+ }
+ mListenersCache = listeners;
+ }
+ }
+
+ /**
+ * Returns the total duration of this Transition. This is only valid after the transition has
+ * been started.
+ */
+ final long getTotalDurationMillis() {
+ return mTotalDuration;
+ }
+
+ /**
+ * Seek the Transition to playTimeMillis.
+ *
+ * @param playTimeMillis The current time (in milliseconds) of the transition. If it is less
+ * than 0, the transition will be set to the beginning. If it is
+ * larger than getTotalDurationMillis(), it will be set to the end.
+ * @param lastPlayTimeMillis The previous play time that was set. This can be negative to
+ * indicate that the transition hasn't been played yet or larger
+ * than getTotalDurationMillis() to indicate that it is playing
+ * backwards.
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ void setCurrentPlayTimeMillis(long playTimeMillis, long lastPlayTimeMillis) {
+ long duration = getTotalDurationMillis();
+ boolean isReversed = playTimeMillis < lastPlayTimeMillis;
+ if ((lastPlayTimeMillis < 0 && playTimeMillis >= 0)
+ || (lastPlayTimeMillis > duration && playTimeMillis <= duration)) {
+ mEnded = false;
+ notifyListeners(TransitionNotification.ON_START, isReversed);
+ }
+ for (int i = 0; i < mCurrentAnimators.size(); i++) {
+ Animator animator = mCurrentAnimators.get(i);
+ long animDuration = Impl26.getTotalDuration(animator);
+ long playTime = Math.min(Math.max(0, playTimeMillis), animDuration);
+ Impl26.setCurrentPlayTime(animator, playTime);
+ }
+
+ if ((playTimeMillis > duration && lastPlayTimeMillis <= duration)
+ || (playTimeMillis < 0 && lastPlayTimeMillis >= 0)
+ ) {
+ if (playTimeMillis > duration) {
+ // Only mark it as finished after the end. Otherwise, it won't
+ // receive pause/resume calls.
+ mEnded = true;
+ }
+ notifyListeners(TransitionNotification.ON_END, isReversed);
+ }
+ }
+
String toString(String indent) {
- String result = indent + getClass().getSimpleName() + "@"
- + Integer.toHexString(hashCode()) + ": ";
+ StringBuilder result = new StringBuilder(indent)
+ .append(getClass().getSimpleName())
+ .append("@")
+ .append(Integer.toHexString(hashCode()))
+ .append(": ");
if (mDuration != -1) {
- result += "dur(" + mDuration + ") ";
+ result.append("dur(")
+ .append(mDuration)
+ .append(") ");
}
if (mStartDelay != -1) {
- result += "dly(" + mStartDelay + ") ";
+ result.append("dly(")
+ .append(mStartDelay)
+ .append(") ");
}
if (mInterpolator != null) {
- result += "interp(" + mInterpolator + ") ";
+ result.append("interp(")
+ .append(mInterpolator)
+ .append(") ");
}
if (mTargetIds.size() > 0 || mTargets.size() > 0) {
- result += "tgts(";
+ result.append("tgts(");
if (mTargetIds.size() > 0) {
for (int i = 0; i < mTargetIds.size(); ++i) {
if (i > 0) {
- result += ", ";
+ result.append(", ");
}
- result += mTargetIds.get(i);
+ result.append(mTargetIds.get(i));
}
}
if (mTargets.size() > 0) {
for (int i = 0; i < mTargets.size(); ++i) {
if (i > 0) {
- result += ", ";
+ result.append(", ");
}
- result += mTargets.get(i);
+ result.append(mTargets.get(i));
}
}
- result += ")";
+ result.append(")");
}
- return result;
+ return result.toString();
}
/**
@@ -2273,6 +2448,16 @@
void onTransitionStart(@NonNull Transition transition);
/**
+ * Notification about the start of the transition.
+ *
+ * @param transition The started transition.
+ * @param isReverse {@code true} when seeking the transition backwards from the end.
+ */
+ default void onTransitionStart(@NonNull Transition transition, boolean isReverse) {
+ onTransitionStart(transition);
+ }
+
+ /**
* Notification about the end of the transition. Canceled transitions
* will always notify listeners of both the cancellation and end
* events. That is, {@link #onTransitionEnd(Transition)} is always called,
@@ -2284,6 +2469,21 @@
void onTransitionEnd(@NonNull Transition transition);
/**
+ * Notification about the end of the transition. Canceled transitions
+ * will always notify listeners of both the cancellation and end
+ * events. That is, {@link #onTransitionEnd(Transition, boolean)} is always called,
+ * regardless of whether the transition was canceled or played
+ * through to completion. Canceled transitions will have {@code isReverse}
+ * set to {@code false}.
+ *
+ * @param transition The transition which reached its end.
+ * @param isReverse {@code true} when seeking the transition backwards past the start.
+ */
+ default void onTransitionEnd(@NonNull Transition transition, boolean isReverse) {
+ onTransitionEnd(transition);
+ }
+
+ /**
* Notification about the cancellation of the transition.
* Note that cancel may be called by a parent {@link TransitionSet} on
* a child transition which has not yet started. This allows the child
@@ -2338,13 +2538,16 @@
Transition mTransition;
+ Animator mAnimator;
+
AnimationInfo(View view, String name, Transition transition, WindowIdImpl windowId,
- TransitionValues values) {
+ TransitionValues values, Animator animator) {
mView = view;
mName = name;
mValues = values;
mWindowId = windowId;
mTransition = transition;
+ mAnimator = animator;
}
}
@@ -2420,4 +2623,212 @@
public abstract Rect onGetEpicenter(@NonNull Transition transition);
}
+ /**
+ * Used internally by notifyListener() to call TransitionListener methods for this transition.
+ */
+ interface TransitionNotification {
+ /**
+ * Make a call on a TransitionListener
+ * @param listener The listener that this should call on.
+ * @param transition The Transition making the call.
+ */
+ void notifyListener(
+ @NonNull TransitionListener listener,
+ @NonNull Transition transition,
+ boolean isReversed
+ );
+
+ /**
+ * Call for TransitionListener#onTransitionStart()
+ */
+ TransitionNotification ON_START = TransitionListener::onTransitionStart;
+
+ /**
+ * Call for TransitionListener#onTransitionEnd()
+ */
+ TransitionNotification ON_END = TransitionListener::onTransitionEnd;
+
+ /**
+ * Call for TransitionListener#onTransitionCancel()
+ */
+ TransitionNotification ON_CANCEL =
+ (listener, transition, isReversed) -> listener.onTransitionCancel(transition);
+
+ /**
+ * Call for TransitionListener#onTransitionPause()
+ */
+ TransitionNotification ON_PAUSE =
+ (listener, transition, isReversed) -> listener.onTransitionPause(transition);
+
+ /**
+ * Call for TransitionListener#onTransitionResume()
+ */
+ TransitionNotification ON_RESUME =
+ (listener, transition, isReversed) -> listener.onTransitionResume(transition);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private static class Impl26 {
+ @DoNotInline
+ static long getTotalDuration(Animator animator) {
+ return animator.getTotalDuration();
+ }
+
+ @DoNotInline
+ static void setCurrentPlayTime(Animator animator, long playTimeMillis) {
+ ((AnimatorSet) animator).setCurrentPlayTime(playTimeMillis);
+ }
+ }
+
+ /**
+ * Internal implementation of TransitionSeekController.
+ */
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ class SeekController extends TransitionListenerAdapter implements TransitionSeekController,
+ ValueAnimator.AnimatorUpdateListener {
+ private long mCurrentPlayTime = -1;
+ private ArrayList<Consumer<TransitionSeekController>> mOnReadyListeners = null;
+ private boolean mIsReady;
+ private boolean mIsCanceled;
+
+ private ValueAnimator mAnimator;
+ private boolean mIsAnimatingReversed;
+
+ @Override
+ public long getDurationMillis() {
+ return Transition.this.getTotalDurationMillis();
+ }
+
+ @Override
+ public long getCurrentPlayTimeMillis() {
+ return Math.min(getDurationMillis(), Math.max(0, mCurrentPlayTime));
+ }
+
+ @Override
+ public boolean isReady() {
+ return mIsReady;
+ }
+
+ public void ready() {
+ mIsReady = true;
+ if (mOnReadyListeners != null) {
+ ArrayList<Consumer<TransitionSeekController>> onReadyListeners = mOnReadyListeners;
+ mOnReadyListeners = null;
+ for (int i = 0; i < onReadyListeners.size(); i++) {
+ onReadyListeners.get(i).accept(this);
+ }
+ }
+ }
+
+ @Override
+ public void setCurrentPlayTimeMillis(long playTimeMillis) {
+ if (mAnimator != null) {
+ throw new IllegalStateException("setCurrentPlayTimeMillis() called after animation "
+ + "has been started");
+ }
+ if (playTimeMillis == mCurrentPlayTime) {
+ return; // no change
+ }
+
+ if (!mIsCanceled) {
+ if (playTimeMillis == 0 && mCurrentPlayTime > 0) {
+ // Force the transition to end
+ playTimeMillis = -1;
+ } else {
+ long duration = getDurationMillis();
+ // Force the transition to the end
+ if (playTimeMillis == duration && mCurrentPlayTime < duration) {
+ playTimeMillis = duration + 1;
+ }
+ }
+ if (playTimeMillis != mCurrentPlayTime) {
+ Transition.this.setCurrentPlayTimeMillis(playTimeMillis, mCurrentPlayTime);
+ mCurrentPlayTime = playTimeMillis;
+ }
+ }
+ }
+
+ @Override
+ public void addOnReadyListener(
+ @NonNull Consumer<TransitionSeekController> onReadyListener
+ ) {
+ if (isReady()) {
+ onReadyListener.accept(this);
+ return;
+ }
+ if (mOnReadyListeners == null) {
+ mOnReadyListeners = new ArrayList<>();
+ }
+ mOnReadyListeners.add(onReadyListener);
+ }
+
+ @Override
+ public void removeOnReadyListener(
+ @NonNull Consumer<TransitionSeekController> onReadyListener
+ ) {
+ if (mOnReadyListeners != null) {
+ mOnReadyListeners.remove(onReadyListener);
+ if (mOnReadyListeners.isEmpty()) {
+ mOnReadyListeners = null;
+ }
+ }
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ mIsCanceled = true;
+ }
+
+ @Override
+ public void onAnimationUpdate(@NonNull ValueAnimator valueAnimator) {
+ long time = Math.max(-1,
+ Math.min(getDurationMillis() + 1, mAnimator.getCurrentPlayTime())
+ );
+ if (mIsAnimatingReversed) {
+ time = getDurationMillis() - time;
+ }
+ Transition.this.setCurrentPlayTimeMillis(time, mCurrentPlayTime);
+ mCurrentPlayTime = time;
+ }
+
+ private void createAnimator() {
+ long duration = getDurationMillis() + 1;
+ mAnimator = ValueAnimator.ofInt((int) duration);
+ mAnimator.setInterpolator(null);
+ mAnimator.setDuration(duration);
+ mAnimator.addUpdateListener(this);
+ }
+
+ @Override
+ public void animateToEnd() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ final long duration = getDurationMillis();
+ if (mCurrentPlayTime > duration) {
+ return; // we're already at the end
+ }
+ createAnimator();
+ mIsAnimatingReversed = false;
+ mAnimator.setCurrentPlayTime(mCurrentPlayTime);
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ notifyListeners(TransitionNotification.ON_END, false);
+ }
+ });
+ mAnimator.start();
+ }
+
+ @Override
+ public void animateToStart() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ createAnimator();
+ mAnimator.setCurrentPlayTime(getDurationMillis() - mCurrentPlayTime);
+ mIsAnimatingReversed = true;
+ mAnimator.start();
+ }
+ }
}
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionManager.java b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
index ccfd27b..ea86ef7 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionManager.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
@@ -24,7 +24,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
+import androidx.core.os.BuildCompat;
import androidx.core.view.ViewCompat;
import java.lang.ref.WeakReference;
@@ -193,6 +196,7 @@
}
}
+ @VisibleForTesting
static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
sRunningTransitions.get();
@@ -419,6 +423,60 @@
}
/**
+ * Create a {@link TransitionSeekController} to allow seeking an animation to a new
+ * scene defined by all changes within the given scene root between calling this method and
+ * the next rendered frame. Calling this method causes TransitionManager to capture current
+ * values in the scene root and then post a request to run a transition on the next frame.
+ * At that time, the new values in the scene root will be captured and changes
+ * will be animated. There is no need to create a Scene; it is implied by
+ * changes which take place between calling this method and the next frame when
+ * the transition begins.
+ *
+ * <p>Calling this method several times before the next frame (for example, if
+ * unrelated code also wants to make dynamic changes and run a transition on
+ * the same scene root), only the first call will trigger capturing values
+ * and exiting the current scene. Subsequent calls to the method with the
+ * same scene root during the same frame will be ignored.</p>
+ *
+ * @param sceneRoot The root of the View hierarchy to run the transition on.
+ * @param transition The transition to use for this change.
+ * @return a {@link TransitionSeekController} that can be used control the animation to the
+ * destination scene. {@code null} is returned when seeking is not supported on the scene,
+ * either because it is running on {@link android.os.Build.VERSION_CODES.TIRAMISU} or earlier,
+ * another Transition is being captured for {@code sceneRoot}, or {@code sceneRoot} hasn't
+ * had a layout yet.
+ * @throws IllegalArgumentException if {@code transition} returns {@code false} from
+ * {@link Transition#isSeekingSupported()}.
+ */
+ @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+ @Nullable
+ public static TransitionSeekController controlDelayedTransition(
+ @NonNull final ViewGroup sceneRoot,
+ @NonNull Transition transition
+ ) {
+ if (sPendingTransitions.contains(sceneRoot) || !ViewCompat.isLaidOut(sceneRoot)
+ || !BuildCompat.isAtLeastU()) {
+ return null;
+ }
+ if (!transition.isSeekingSupported()) {
+ throw new IllegalArgumentException("The Transition must support seeking.");
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "controlDelayedTransition: root, transition = "
+ + sceneRoot + ", " + transition);
+ }
+ sPendingTransitions.add(sceneRoot);
+ final Transition transitionClone = transition.clone();
+ final TransitionSet set = new TransitionSet();
+ set.addTransition(transitionClone);
+ sceneChangeSetup(sceneRoot, set);
+ Scene.setCurrentScene(sceneRoot, null);
+ sceneChangeRunTransition(sceneRoot, set);
+ sceneRoot.invalidate();
+ return set.createSeekController();
+ }
+
+ /**
* Ends all pending and ongoing transitions on the specified scene root.
*
* @param sceneRoot The root of the View hierarchy to end transitions on.
@@ -435,5 +493,4 @@
}
}
}
-
}
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
new file mode 100644
index 0000000..bcede11
--- /dev/null
+++ b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.transition;
+
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Consumer;
+
+/**
+ * Returned from {@link TransitionManager#controlDelayedTransition(ViewGroup, Transition)}
+ * to allow manually controlling the animations within a Transition using
+ * {@link #setCurrentPlayTimeMillis(long)}. The transition will be ready to seek when
+ * {@link #isReady()} is {@code true}.
+ */
+public interface TransitionSeekController {
+ /**
+ * @return The total duration, in milliseconds, of the Transition's animations.
+ */
+ long getDurationMillis();
+
+ /**
+ * @return The time, in milliseconds, of the animation. This will be between 0
+ * and {@link #getDurationMillis()}.
+ */
+ long getCurrentPlayTimeMillis();
+
+ /**
+ * Returns {@code true} when the Transition is ready to seek or {@code false}
+ * when the Transition's animations have yet to be built.
+ */
+ boolean isReady();
+
+ /**
+ * Runs the animation backwards toward the start. {@link #setCurrentPlayTimeMillis(long)}
+ * will not be allowed after executing this. When the animation completes,
+ * {@link androidx.transition.Transition.TransitionListener#onTransitionEnd(Transition)}
+ * will be called with the {@code isReverse} parameter {@code true}.
+ *
+ * The developer will likely want to run
+ * {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)} to set the state
+ * back to the beginning state after it ends.
+ *
+ * After calling this, {@link #setCurrentPlayTimeMillis(long)} may not be called.
+ */
+ void animateToStart();
+
+ /**
+ * Runs the animation forwards toward the end. {@link #setCurrentPlayTimeMillis(long)}
+ * will not be allowed after executing this. When the animation completes,
+ * {@link androidx.transition.Transition.TransitionListener#onTransitionEnd(Transition)}
+ * will be called with the {@code isReverse} parameter {@code false}.
+ *
+ * After the Transition ends, the state will reach the final state set after
+ * {@link TransitionManager#controlDelayedTransition(ViewGroup, Transition)}.
+ *
+ * After calling this, {@link #setCurrentPlayTimeMillis(long)} may not be called.
+ */
+ void animateToEnd();
+
+ /**
+ * Sets the position of the Transition's animation. {@code playTimeMillis} should be
+ * between 0 and {@link #getDurationMillis()}. This should not be called when
+ * {@link #isReady()} is {@code false}.
+ *
+ * @param playTimeMillis The time, between 0 and {@link #getDurationMillis()} that the
+ * animation should play.
+ */
+ void setCurrentPlayTimeMillis(long playTimeMillis);
+
+ /**
+ * Adds a listener to know when {@link #isReady()} is {@code true}. The listener will
+ * be removed once notified as {@link #isReady()} can only be made true once. If
+ * {@link #isReady()} is already {@code true}, then it will be notified immediately.
+ *
+ * @param onReadyListener The listener to be notified when the Transition is ready.
+ */
+ void addOnReadyListener(@NonNull Consumer<TransitionSeekController> onReadyListener);
+
+ /**
+ * Removes {@code onReadyListener} that was previously added in
+ * {@link #addOnReadyListener(Consumer)} so that it won't be called.
+ *
+ * @param onReadyListener The listener to be removed so that it won't be notified when ready.
+ */
+ void removeOnReadyListener(@NonNull Consumer<TransitionSeekController> onReadyListener);
+}
+
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionSet.java b/transition/transition/src/main/java/androidx/transition/TransitionSet.java
index b1a1a3e..c875b2b 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionSet.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionSet.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.util.AndroidRuntimeException;
import android.util.AttributeSet;
import android.view.View;
@@ -31,6 +32,7 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.content.res.TypedArrayUtils;
@@ -77,7 +79,7 @@
*/
private static final int FLAG_CHANGE_EPICENTER = 0x08;
- private ArrayList<Transition> mTransitions = new ArrayList<>();
+ ArrayList<Transition> mTransitions = new ArrayList<>();
private boolean mPlayTogether = true;
@SuppressWarnings("WeakerAccess") /* synthetic access */
int mCurrentListeners;
@@ -515,6 +517,124 @@
}
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ @Override
+ void prepareAnimatorsForSeeking() {
+ mTotalDuration = 0;
+ TransitionListenerAdapter listener = new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ mTransitions.remove(transition);
+ if (mTransitions.isEmpty()) {
+ notifyListeners(TransitionNotification.ON_CANCEL, false);
+ if (!mEnded) {
+ mEnded = true;
+ notifyListeners(TransitionNotification.ON_END, false);
+ }
+ }
+ }
+ };
+ for (int i = 0; i < mTransitions.size(); ++i) {
+ Transition transition = mTransitions.get(i);
+ transition.addListener(listener);
+ transition.prepareAnimatorsForSeeking();
+ long duration = transition.getTotalDurationMillis();
+ if (mPlayTogether) {
+ mTotalDuration = Math.max(mTotalDuration, duration);
+ } else {
+ transition.mSeekOffsetInParent = mTotalDuration;
+ mTotalDuration += duration;
+ }
+ }
+ }
+
+ /**
+ * Returns the index of the Transition that is playing at playTime. If no such transition
+ * exists, either because that Transition has been canceled or the TransitionSet is empty,
+ * the index of the one prior, or 0 will be returned.
+ */
+ private int indexOfTransition(long playTime) {
+ for (int i = 1; i < mTransitions.size(); i++) {
+ Transition transition = mTransitions.get(i);
+ if (transition.mSeekOffsetInParent > playTime) {
+ return i - 1;
+ }
+ }
+ return mTransitions.size() - 1;
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ @Override
+ void setCurrentPlayTimeMillis(long playTimeMillis, long lastPlayTimeMillis) {
+ long duration = getTotalDurationMillis();
+ if (mParent != null && ((playTimeMillis < 0 && lastPlayTimeMillis < 0)
+ || (playTimeMillis > duration && lastPlayTimeMillis > duration))
+ ) {
+ return;
+ }
+ boolean isReverse = playTimeMillis < lastPlayTimeMillis;
+ if ((playTimeMillis >= 0 && lastPlayTimeMillis < 0)
+ || (playTimeMillis <= duration && lastPlayTimeMillis > duration)
+ ) {
+ mEnded = false;
+ notifyListeners(TransitionNotification.ON_START, isReverse);
+ }
+ if (mPlayTogether) {
+ for (int i = 0; i < mTransitions.size(); i++) {
+ Transition transition = mTransitions.get(i);
+ transition.setCurrentPlayTimeMillis(playTimeMillis, lastPlayTimeMillis);
+ }
+ } else {
+ // find the Transition that lastPlayTimeMillis was using
+ int startIndex = indexOfTransition(lastPlayTimeMillis);
+
+ if (playTimeMillis >= lastPlayTimeMillis) {
+ // move forward through transitions
+ for (int i = startIndex; i < mTransitions.size(); i++) {
+ Transition transition = mTransitions.get(i);
+ long transitionStart = transition.mSeekOffsetInParent;
+ long playTime = playTimeMillis - transitionStart;
+ if (playTime < 0) {
+ break; // went past
+ }
+ long lastPlayTime = lastPlayTimeMillis - transitionStart;
+ transition.setCurrentPlayTimeMillis(playTime, lastPlayTime);
+ }
+ } else {
+ // move backwards through transitions
+ for (int i = startIndex; i >= 0; i--) {
+ Transition transition = mTransitions.get(i);
+ long transitionStart = transition.mSeekOffsetInParent;
+ long playTime = playTimeMillis - transitionStart;
+ long lastPlayTime = lastPlayTimeMillis - transitionStart;
+ transition.setCurrentPlayTimeMillis(playTime, lastPlayTime);
+ if (playTime >= 0) {
+ break;
+ }
+ }
+ }
+ }
+ if (mParent != null && ((playTimeMillis > duration && lastPlayTimeMillis <= duration)
+ || (playTimeMillis < 0 && lastPlayTimeMillis >= 0))
+ ) {
+ if (playTimeMillis > duration) {
+ mEnded = true;
+ }
+ notifyListeners(TransitionNotification.ON_END, isReverse);
+ }
+ }
+
+ @Override
+ public boolean isSeekingSupported() {
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; i++) {
+ if (!mTransitions.get(i).isSeekingSupported()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
if (isValidTarget(transitionValues.view)) {
diff --git a/transition/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java b/transition/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
index 7a24a33..114674c 100644
--- a/transition/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
+++ b/transition/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
@@ -77,7 +77,6 @@
startPosX, startPosY, terminalX, terminalY);
transition.addListener(listener);
anim.addListener(listener);
- AnimatorUtils.addPauseListener(anim, listener);
anim.setInterpolator(interpolator);
return anim;
}
@@ -87,20 +86,17 @@
private final View mViewInHierarchy;
private final View mMovingView;
- private final int mStartX;
- private final int mStartY;
private int[] mTransitionPosition;
private float mPausedX;
private float mPausedY;
private final float mTerminalX;
private final float mTerminalY;
+ private boolean mIsAnimationCancelCalled;
TransitionPositionListener(View movingView, View viewInHierarchy,
int startX, int startY, float terminalX, float terminalY) {
mMovingView = movingView;
mViewInHierarchy = viewInHierarchy;
- mStartX = startX - Math.round(mMovingView.getTranslationX());
- mStartY = startY - Math.round(mMovingView.getTranslationY());
mTerminalX = terminalX;
mTerminalY = terminalY;
mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transition_position);
@@ -111,26 +107,8 @@
@Override
public void onAnimationCancel(Animator animation) {
- if (mTransitionPosition == null) {
- mTransitionPosition = new int[2];
- }
- mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
- mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
- mViewInHierarchy.setTag(R.id.transition_position, mTransitionPosition);
- }
-
- @Override
- public void onAnimationPause(Animator animator) {
- mPausedX = mMovingView.getTranslationX();
- mPausedY = mMovingView.getTranslationY();
- mMovingView.setTranslationX(mTerminalX);
- mMovingView.setTranslationY(mTerminalY);
- }
-
- @Override
- public void onAnimationResume(Animator animator) {
- mMovingView.setTranslationX(mPausedX);
- mMovingView.setTranslationY(mPausedY);
+ setInterruptedPosition();
+ mIsAnimationCancelCalled = true;
}
@Override
@@ -138,22 +116,48 @@
}
@Override
+ public void onTransitionEnd(@NonNull Transition transition, boolean isReverse) {
+ if (!isReverse) {
+ mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
+ }
+ }
+
+ @Override
public void onTransitionEnd(@NonNull Transition transition) {
- mMovingView.setTranslationX(mTerminalX);
- mMovingView.setTranslationY(mTerminalY);
- transition.removeListener(this);
}
@Override
public void onTransitionCancel(@NonNull Transition transition) {
+ if (!mIsAnimationCancelCalled) {
+ setInterruptedPosition();
+ }
+ mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
+ int[] pos = new int[2];
+ mMovingView.getLocationOnScreen(pos);
}
@Override
public void onTransitionPause(@NonNull Transition transition) {
+ mPausedX = mMovingView.getTranslationX();
+ mPausedY = mMovingView.getTranslationY();
+ mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
}
@Override
public void onTransitionResume(@NonNull Transition transition) {
+ mMovingView.setTranslationX(mPausedX);
+ mMovingView.setTranslationY(mPausedY);
+ }
+
+ private void setInterruptedPosition() {
+ if (mTransitionPosition == null) {
+ mTransitionPosition = new int[2];
+ }
+ mMovingView.getLocationOnScreen(mTransitionPosition);
+ mViewInHierarchy.setTag(R.id.transition_position, mTransitionPosition);
}
}
diff --git a/transition/transition/src/main/java/androidx/transition/Visibility.java b/transition/transition/src/main/java/androidx/transition/Visibility.java
index a9f85bd..8b3fc2e 100644
--- a/transition/transition/src/main/java/androidx/transition/Visibility.java
+++ b/transition/transition/src/main/java/androidx/transition/Visibility.java
@@ -436,31 +436,13 @@
ViewGroupUtils.getOverlay(sceneRoot).remove(overlayView);
} else {
startView.setTag(R.id.save_overlay_view, overlayView);
- final View finalOverlayView = overlayView;
- final ViewGroup overlayHost = sceneRoot;
- addListener(new TransitionListenerAdapter() {
- @Override
- public void onTransitionPause(@NonNull Transition transition) {
- ViewGroupUtils.getOverlay(overlayHost).remove(finalOverlayView);
- }
+ OverlayListener listener = new OverlayListener(sceneRoot, overlayView,
+ startView);
- @Override
- public void onTransitionResume(@NonNull Transition transition) {
- if (finalOverlayView.getParent() == null) {
- ViewGroupUtils.getOverlay(overlayHost).add(finalOverlayView);
- } else {
- cancel();
- }
- }
-
- @Override
- public void onTransitionEnd(@NonNull Transition transition) {
- startView.setTag(R.id.save_overlay_view, null);
- ViewGroupUtils.getOverlay(overlayHost).remove(finalOverlayView);
- transition.removeListener(this);
- }
- });
+ animator.addListener(listener);
+ AnimatorUtils.addPauseListener(animator, listener);
+ getRootTransition().addListener(listener);
}
}
return animator;
@@ -474,8 +456,7 @@
DisappearListener disappearListener = new DisappearListener(viewToKeep,
endVisibility, true);
animator.addListener(disappearListener);
- AnimatorUtils.addPauseListener(animator, disappearListener);
- addListener(disappearListener);
+ getRootTransition().addListener(disappearListener);
} else {
ViewUtils.setTransitionVisibility(viewToKeep, originalVisibility);
}
@@ -525,7 +506,7 @@
}
private static class DisappearListener extends AnimatorListenerAdapter
- implements TransitionListener, AnimatorUtils.AnimatorPauseListenerCompat {
+ implements TransitionListener {
private final View mView;
private final int mFinalVisibility;
@@ -544,24 +525,6 @@
suppressLayout(true);
}
- // This overrides both AnimatorListenerAdapter and
- // AnimatorUtilsApi14.AnimatorPauseListenerCompat
- @Override
- public void onAnimationPause(Animator animation) {
- if (!mCanceled) {
- ViewUtils.setTransitionVisibility(mView, mFinalVisibility);
- }
- }
-
- // This overrides both AnimatorListenerAdapter and
- // AnimatorUtilsApi14.AnimatorPauseListenerCompat
- @Override
- public void onAnimationResume(Animator animation) {
- if (!mCanceled) {
- ViewUtils.setTransitionVisibility(mView, View.VISIBLE);
- }
- }
-
@Override
public void onAnimationCancel(Animator animation) {
mCanceled = true;
@@ -581,13 +544,29 @@
}
@Override
+ public void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
+ if (isReverse) {
+ ViewUtils.setTransitionVisibility(mView, View.VISIBLE);
+ if (mParent != null) {
+ mParent.invalidate();
+ }
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
+ if (!isReverse) {
+ hideViewWhenNotCanceled();
+ }
+ }
+
+ @Override
public void onTransitionStart(@NonNull Transition transition) {
// Do nothing
}
@Override
public void onTransitionEnd(@NonNull Transition transition) {
- hideViewWhenNotCanceled();
transition.removeListener(this);
}
@@ -598,11 +577,17 @@
@Override
public void onTransitionPause(@NonNull Transition transition) {
suppressLayout(false);
+ if (!mCanceled) {
+ ViewUtils.setTransitionVisibility(mView, mFinalVisibility);
+ }
}
@Override
public void onTransitionResume(@NonNull Transition transition) {
suppressLayout(true);
+ if (!mCanceled) {
+ ViewUtils.setTransitionVisibility(mView, View.VISIBLE);
+ }
}
private void hideViewWhenNotCanceled() {
@@ -624,4 +609,83 @@
}
}
}
+
+ private class OverlayListener extends AnimatorListenerAdapter implements TransitionListener,
+ AnimatorUtils.AnimatorPauseListenerCompat {
+ private final ViewGroup mOverlayHost;
+ private final View mOverlayView;
+ private final View mStartView;
+ private boolean mHasOverlay = true;
+
+ OverlayListener(ViewGroup overlayHost, View overlayView, View startView) {
+ mOverlayHost = overlayHost;
+ mOverlayView = overlayView;
+ mStartView = startView;
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ ViewGroupUtils.getOverlay(mOverlayHost).remove(mOverlayView);
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ if (mOverlayView.getParent() == null) {
+ ViewGroupUtils.getOverlay(mOverlayHost).add(mOverlayView);
+ } else {
+ cancel();
+ }
+ }
+
+ @Override
+ public void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
+ if (isReverse) {
+ mStartView.setTag(R.id.save_overlay_view, mOverlayView);
+ ViewGroupUtils.getOverlay(mOverlayHost).add(mOverlayView);
+ mHasOverlay = true;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeFromOverlay();
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
+ if (!isReverse) {
+ removeFromOverlay();
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ if (mHasOverlay) {
+ removeFromOverlay();
+ }
+ }
+
+ private void removeFromOverlay() {
+ mStartView.setTag(R.id.save_overlay_view, null);
+ ViewGroupUtils.getOverlay(mOverlayHost).remove(mOverlayView);
+ mHasOverlay = false;
+ }
+ }
}
diff --git a/transition/transition/src/main/res/values/ids.xml b/transition/transition/src/main/res/values/ids.xml
index b9dd584..c2f6195 100644
--- a/transition/transition/src/main/res/values/ids.xml
+++ b/transition/transition/src/main/res/values/ids.xml
@@ -20,6 +20,8 @@
<item name="transition_layout_save" type="id"/>
<item name="transition_position" type="id"/>
<item name="transition_transform" type="id"/>
+ <item name="transition_image_transform" type="id"/>
+ <item name="transition_clip" type="id"/>
<item name="parent_matrix" type="id"/>
<item name="ghost_view" type="id"/>
<item name="ghost_view_holder" type="id"/>
diff --git a/viewpager/viewpager/api/api_lint.ignore b/viewpager/viewpager/api/api_lint.ignore
index 1c07b48..908ecb2 100644
--- a/viewpager/viewpager/api/api_lint.ignore
+++ b/viewpager/viewpager/api/api_lint.ignore
@@ -9,18 +9,12 @@
Symmetric method for `setDrawFullUnderline` must be named `isDrawFullUnderline`; was `getDrawFullUnderline`
-InvalidNullabilityOverride: androidx.viewpager.widget.PagerTabStrip#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.viewpager.widget.ViewPager#draw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `draw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-InvalidNullabilityOverride: androidx.viewpager.widget.ViewPager#onDraw(android.graphics.Canvas) parameter #0:
- Invalid nullability on parameter `canvas` in method `onDraw`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-
-
ListenerInterface: androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener:
Listeners should be an interface, or otherwise renamed Callback: SimpleOnPageChangeListener
+MissingNullability: androidx.viewpager.widget.PagerTabStrip#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.viewpager.widget.PagerTabStrip#onTouchEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `ev` in method `onTouchEvent`
MissingNullability: androidx.viewpager.widget.PagerTabStrip#setBackgroundDrawable(android.graphics.drawable.Drawable) parameter #0:
@@ -39,6 +33,8 @@
Missing nullability on parameter `event` in method `dispatchKeyEvent`
MissingNullability: androidx.viewpager.widget.ViewPager#dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent) parameter #0:
Missing nullability on parameter `event` in method `dispatchPopulateAccessibilityEvent`
+MissingNullability: androidx.viewpager.widget.ViewPager#draw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `draw`
MissingNullability: androidx.viewpager.widget.ViewPager#generateDefaultLayoutParams():
Missing nullability on method `generateDefaultLayoutParams` return
MissingNullability: androidx.viewpager.widget.ViewPager#generateLayoutParams(android.util.AttributeSet):
@@ -49,6 +45,8 @@
Missing nullability on method `generateLayoutParams` return
MissingNullability: androidx.viewpager.widget.ViewPager#generateLayoutParams(android.view.ViewGroup.LayoutParams) parameter #0:
Missing nullability on parameter `p` in method `generateLayoutParams`
+MissingNullability: androidx.viewpager.widget.ViewPager#onDraw(android.graphics.Canvas) parameter #0:
+ Missing nullability on parameter `canvas` in method `onDraw`
MissingNullability: androidx.viewpager.widget.ViewPager#onInterceptTouchEvent(android.view.MotionEvent) parameter #0:
Missing nullability on parameter `ev` in method `onInterceptTouchEvent`
MissingNullability: androidx.viewpager.widget.ViewPager#onRequestFocusInDescendants(int, android.graphics.Rect) parameter #1:
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
index 7d31f03..a4f665f 100644
--- a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
@@ -32,6 +32,7 @@
onCreateCallback(this)
// disable enter animation.
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
@@ -39,6 +40,7 @@
super.finish()
// disable exit animation
+ @Suppress("Deprecation")
overridePendingTransition(0, 0)
}
diff --git a/wear/compose/compose-foundation/api/1.2.0-beta01.txt b/wear/compose/compose-foundation/api/1.2.0-beta01.txt
index 03d2707..91d6749 100644
--- a/wear/compose/compose-foundation/api/1.2.0-beta01.txt
+++ b/wear/compose/compose-foundation/api/1.2.0-beta01.txt
@@ -26,11 +26,6 @@
method public static void basicCurvedText(androidx.wear.compose.foundation.CurvedScope, String text, androidx.wear.compose.foundation.CurvedTextStyle style, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
}
- public final class CompositionLocalsKt {
- method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> getLocalReduceMotion();
- property @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> LocalReduceMotion;
- }
-
public interface CurvedAlignment {
}
@@ -200,61 +195,6 @@
property public final boolean expanded;
}
- @kotlin.RequiresOptIn(message="This Wear Foundation API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearFoundationApi {
- }
-
- public final class HierarchicalFocusCoordinatorKt {
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public fun interface ReduceMotion {
- method @androidx.compose.runtime.Composable public boolean enabled();
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public interface RevealScope {
- method public float getRevealOffset();
- property public abstract float revealOffset;
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public final class RevealState {
- method public suspend Object? animateTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public int getCurrentValue();
- method public float getOffset();
- method public java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> getSwipeAnchors();
- method public int getTargetValue();
- method public boolean isAnimationRunning();
- method public suspend Object? snapTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final int currentValue;
- property public final boolean isAnimationRunning;
- property public final float offset;
- property public final java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> swipeAnchors;
- property public final int targetValue;
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi @kotlin.jvm.JvmInline public final value class RevealValue {
- method public int getValue();
- property public final int value;
- field public static final androidx.wear.compose.foundation.RevealValue.Companion Companion;
- }
-
- public static final class RevealValue.Companion {
- method public int getCovered();
- method public int getRevealed();
- method public int getRevealing();
- property public final int Covered;
- property public final int Revealed;
- property public final int Revealing;
- }
-
- public final class SwipeToRevealKt {
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void SwipeToReveal(kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit> action, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onFullSwipe, optional androidx.wear.compose.foundation.RevealState state, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? additionalAction, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? undoAction, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> createAnchors(optional float coveredAnchor, optional float revealingAnchor, optional float revealedAnchor);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.wear.compose.foundation.RevealState rememberRevealState(optional int initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold, optional java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> anchors);
- }
-
}
package androidx.wear.compose.foundation.lazy {
diff --git a/wear/compose/compose-foundation/api/public_plus_experimental_1.2.0-beta01.txt b/wear/compose/compose-foundation/api/public_plus_experimental_1.2.0-beta01.txt
new file mode 100644
index 0000000..03d2707
--- /dev/null
+++ b/wear/compose/compose-foundation/api/public_plus_experimental_1.2.0-beta01.txt
@@ -0,0 +1,398 @@
+// Signature format: 4.0
+package androidx.wear.compose.foundation {
+
+ @kotlin.jvm.JvmInline public final value class AnchorType {
+ field public static final androidx.wear.compose.foundation.AnchorType.Companion Companion;
+ }
+
+ public static final class AnchorType.Companion {
+ method public float getCenter();
+ method public float getEnd();
+ method public float getStart();
+ property public final float Center;
+ property public final float End;
+ property public final float Start;
+ }
+
+ @androidx.compose.runtime.Stable public interface ArcPaddingValues {
+ method public float calculateAfterPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection, int angularDirection);
+ method public float calculateBeforePadding(androidx.compose.ui.unit.LayoutDirection layoutDirection, int angularDirection);
+ method public float calculateInnerPadding(int radialDirection);
+ method public float calculateOuterPadding(int radialDirection);
+ }
+
+ public final class BasicCurvedTextKt {
+ method public static void basicCurvedText(androidx.wear.compose.foundation.CurvedScope, String text, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow, optional kotlin.jvm.functions.Function0<androidx.wear.compose.foundation.CurvedTextStyle> style);
+ method public static void basicCurvedText(androidx.wear.compose.foundation.CurvedScope, String text, androidx.wear.compose.foundation.CurvedTextStyle style, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
+ }
+
+ public final class CompositionLocalsKt {
+ method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> getLocalReduceMotion();
+ property @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> LocalReduceMotion;
+ }
+
+ public interface CurvedAlignment {
+ }
+
+ @kotlin.jvm.JvmInline public static final value class CurvedAlignment.Angular {
+ field public static final androidx.wear.compose.foundation.CurvedAlignment.Angular.Companion Companion;
+ }
+
+ public static final class CurvedAlignment.Angular.Companion {
+ method public float Custom(float ratio);
+ method public float getCenter();
+ method public float getEnd();
+ method public float getStart();
+ property public final float Center;
+ property public final float End;
+ property public final float Start;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class CurvedAlignment.Radial {
+ field public static final androidx.wear.compose.foundation.CurvedAlignment.Radial.Companion Companion;
+ }
+
+ public static final class CurvedAlignment.Radial.Companion {
+ method public float Custom(float ratio);
+ method public float getCenter();
+ method public float getInner();
+ method public float getOuter();
+ property public final float Center;
+ property public final float Inner;
+ property public final float Outer;
+ }
+
+ public final class CurvedBoxKt {
+ method public static void curvedBox(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedAlignment.Radial? radialAlignment, optional androidx.wear.compose.foundation.CurvedAlignment.Angular? angularAlignment, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> contentBuilder);
+ }
+
+ public final class CurvedColumnKt {
+ method public static void curvedColumn(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedDirection.Radial? radialDirection, optional androidx.wear.compose.foundation.CurvedAlignment.Angular? angularAlignment, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> contentBuilder);
+ }
+
+ public final class CurvedComposableKt {
+ method public static void curvedComposable(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional float radialAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
+ public interface CurvedDirection {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public static final value class CurvedDirection.Angular {
+ field public static final androidx.wear.compose.foundation.CurvedDirection.Angular.Companion Companion;
+ }
+
+ public static final class CurvedDirection.Angular.Companion {
+ method public int getClockwise();
+ method public int getCounterClockwise();
+ method public int getNormal();
+ method public int getReversed();
+ property public final int Clockwise;
+ property public final int CounterClockwise;
+ property public final int Normal;
+ property public final int Reversed;
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public static final value class CurvedDirection.Radial {
+ field public static final androidx.wear.compose.foundation.CurvedDirection.Radial.Companion Companion;
+ }
+
+ public static final class CurvedDirection.Radial.Companion {
+ method public int getInsideOut();
+ method public int getOutsideIn();
+ property public final int InsideOut;
+ property public final int OutsideIn;
+ }
+
+ public final class CurvedDrawKt {
+ method public static androidx.wear.compose.foundation.CurvedModifier angularGradientBackground(androidx.wear.compose.foundation.CurvedModifier, java.util.List<androidx.compose.ui.graphics.Color> colors, optional int cap);
+ method public static androidx.wear.compose.foundation.CurvedModifier angularGradientBackground(androidx.wear.compose.foundation.CurvedModifier, kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional int cap);
+ method public static androidx.wear.compose.foundation.CurvedModifier background(androidx.wear.compose.foundation.CurvedModifier, long color, optional int cap);
+ method public static androidx.wear.compose.foundation.CurvedModifier radialGradientBackground(androidx.wear.compose.foundation.CurvedModifier, java.util.List<androidx.compose.ui.graphics.Color> colors, optional int cap);
+ method public static androidx.wear.compose.foundation.CurvedModifier radialGradientBackground(androidx.wear.compose.foundation.CurvedModifier, kotlin.Pair<java.lang.Float,androidx.compose.ui.graphics.Color>![] colorStops, optional int cap);
+ }
+
+ public final class CurvedLayoutKt {
+ method @androidx.compose.runtime.Composable public static void CurvedLayout(optional androidx.compose.ui.Modifier modifier, optional float anchor, optional float anchorType, optional androidx.wear.compose.foundation.CurvedAlignment.Radial? radialAlignment, optional int angularDirection, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> contentBuilder);
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public sealed interface CurvedModifier {
+ method public default infix androidx.wear.compose.foundation.CurvedModifier then(androidx.wear.compose.foundation.CurvedModifier other);
+ field public static final androidx.wear.compose.foundation.CurvedModifier.Companion Companion;
+ }
+
+ public static final class CurvedModifier.Companion implements androidx.wear.compose.foundation.CurvedModifier {
+ }
+
+ public final class CurvedPaddingKt {
+ method public static androidx.wear.compose.foundation.ArcPaddingValues ArcPaddingValues(float all);
+ method public static androidx.wear.compose.foundation.ArcPaddingValues ArcPaddingValues(optional float radial, optional float angular);
+ method public static androidx.wear.compose.foundation.ArcPaddingValues ArcPaddingValues(optional float outer, optional float inner, optional float before, optional float after);
+ method public static androidx.wear.compose.foundation.CurvedModifier padding(androidx.wear.compose.foundation.CurvedModifier, androidx.wear.compose.foundation.ArcPaddingValues paddingValues);
+ method public static androidx.wear.compose.foundation.CurvedModifier padding(androidx.wear.compose.foundation.CurvedModifier, optional float all);
+ method public static androidx.wear.compose.foundation.CurvedModifier padding(androidx.wear.compose.foundation.CurvedModifier, optional float radial, optional float angular);
+ method public static androidx.wear.compose.foundation.CurvedModifier padding(androidx.wear.compose.foundation.CurvedModifier, float outer, float inner, float before, float after);
+ }
+
+ public final class CurvedParentDataKt {
+ method public static androidx.wear.compose.foundation.CurvedModifier parentDataModifier(androidx.wear.compose.foundation.CurvedModifier, kotlin.jvm.functions.Function1<java.lang.Object,?> modifyParentData);
+ method public static androidx.wear.compose.foundation.CurvedModifier weight(androidx.wear.compose.foundation.CurvedModifier, float weight);
+ }
+
+ public final class CurvedRowKt {
+ method public static void curvedRow(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedAlignment.Radial? radialAlignment, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> contentBuilder);
+ }
+
+ @androidx.compose.foundation.layout.LayoutScopeMarker public final class CurvedScope {
+ }
+
+ public final class CurvedSizeKt {
+ method public static androidx.wear.compose.foundation.CurvedModifier angularSize(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees);
+ method public static androidx.wear.compose.foundation.CurvedModifier angularSizeDp(androidx.wear.compose.foundation.CurvedModifier, float angularWidth);
+ method public static androidx.wear.compose.foundation.CurvedModifier radialSize(androidx.wear.compose.foundation.CurvedModifier, float thickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier size(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees, float thickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier sizeIn(androidx.wear.compose.foundation.CurvedModifier, optional float minSweepDegrees, optional float maxSweepDegrees, optional float minThickness, optional float maxThickness);
+ }
+
+ public final class CurvedTextStyle {
+ ctor public CurvedTextStyle(androidx.compose.ui.text.TextStyle style);
+ ctor @Deprecated public CurvedTextStyle(optional long background, optional long color, optional long fontSize);
+ ctor public CurvedTextStyle(optional long background, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis);
+ method @Deprecated public androidx.wear.compose.foundation.CurvedTextStyle copy(optional long background, optional long color, optional long fontSize);
+ method public androidx.wear.compose.foundation.CurvedTextStyle copy(optional long background, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis);
+ method public long getBackground();
+ method public long getColor();
+ method public androidx.compose.ui.text.font.FontFamily? getFontFamily();
+ method public long getFontSize();
+ method public androidx.compose.ui.text.font.FontStyle? getFontStyle();
+ method public androidx.compose.ui.text.font.FontSynthesis? getFontSynthesis();
+ method public androidx.compose.ui.text.font.FontWeight? getFontWeight();
+ method public androidx.wear.compose.foundation.CurvedTextStyle merge(optional androidx.wear.compose.foundation.CurvedTextStyle? other);
+ method public operator androidx.wear.compose.foundation.CurvedTextStyle plus(androidx.wear.compose.foundation.CurvedTextStyle other);
+ property public final long background;
+ property public final long color;
+ property public final androidx.compose.ui.text.font.FontFamily? fontFamily;
+ property public final long fontSize;
+ property public final androidx.compose.ui.text.font.FontStyle? fontStyle;
+ property public final androidx.compose.ui.text.font.FontSynthesis? fontSynthesis;
+ property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
+ }
+
+ public final class ExpandableItemsDefaults {
+ method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
+ method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
+ property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
+ property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
+ field public static final androidx.wear.compose.foundation.ExpandableItemsDefaults INSTANCE;
+ }
+
+ public final class ExpandableKt {
+ method public static void expandableButton(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableState state, optional Object? key, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
+ method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.ExpandableState rememberExpandableState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
+ }
+
+ public final class ExpandableState {
+ method public float getExpandProgress();
+ method public boolean isExpanded();
+ method public void setExpanded(boolean);
+ property public final float expandProgress;
+ property public final boolean expanded;
+ }
+
+ @kotlin.RequiresOptIn(message="This Wear Foundation API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearFoundationApi {
+ }
+
+ public final class HierarchicalFocusCoordinatorKt {
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
+ }
+
+ @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public fun interface ReduceMotion {
+ method @androidx.compose.runtime.Composable public boolean enabled();
+ }
+
+ @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public interface RevealScope {
+ method public float getRevealOffset();
+ property public abstract float revealOffset;
+ }
+
+ @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public final class RevealState {
+ method public suspend Object? animateTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public int getCurrentValue();
+ method public float getOffset();
+ method public java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> getSwipeAnchors();
+ method public int getTargetValue();
+ method public boolean isAnimationRunning();
+ method public suspend Object? snapTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final int currentValue;
+ property public final boolean isAnimationRunning;
+ property public final float offset;
+ property public final java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> swipeAnchors;
+ property public final int targetValue;
+ }
+
+ @androidx.wear.compose.foundation.ExperimentalWearFoundationApi @kotlin.jvm.JvmInline public final value class RevealValue {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.wear.compose.foundation.RevealValue.Companion Companion;
+ }
+
+ public static final class RevealValue.Companion {
+ method public int getCovered();
+ method public int getRevealed();
+ method public int getRevealing();
+ property public final int Covered;
+ property public final int Revealed;
+ property public final int Revealing;
+ }
+
+ public final class SwipeToRevealKt {
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void SwipeToReveal(kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit> action, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onFullSwipe, optional androidx.wear.compose.foundation.RevealState state, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? additionalAction, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? undoAction, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> createAnchors(optional float coveredAnchor, optional float revealingAnchor, optional float revealedAnchor);
+ method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.wear.compose.foundation.RevealState rememberRevealState(optional int initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold, optional java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> anchors);
+ }
+
+}
+
+package androidx.wear.compose.foundation.lazy {
+
+ @androidx.compose.runtime.Immutable public final class AutoCenteringParams {
+ ctor public AutoCenteringParams(optional int itemIndex, optional int itemOffset);
+ }
+
+ public final class ScalingLazyColumnDefaults {
+ method public androidx.wear.compose.foundation.lazy.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.wear.compose.foundation.lazy.ScalingLazyListState state, optional float snapOffset, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
+ field public static final androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults INSTANCE;
+ }
+
+ public final class ScalingLazyColumnKt {
+ method @androidx.compose.runtime.Composable public static void ScalingLazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.lazy.ScalingLazyListState 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, optional boolean userScrollEnabled, optional androidx.wear.compose.foundation.lazy.ScalingParams scalingParams, optional int anchorType, optional androidx.wear.compose.foundation.lazy.AutoCenteringParams? autoCentering, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.ScalingLazyListItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,?>? key, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.ScalingLazyListItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, kotlin.jvm.functions.Function3<? super androidx.wear.compose.foundation.lazy.ScalingLazyListItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, kotlin.jvm.functions.Function3<? super androidx.wear.compose.foundation.lazy.ScalingLazyListItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class ScalingLazyListAnchorType {
+ field public static final androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType.Companion Companion;
+ }
+
+ public static final class ScalingLazyListAnchorType.Companion {
+ method public int getItemCenter();
+ method public int getItemStart();
+ property public final int ItemCenter;
+ property public final int ItemStart;
+ }
+
+ public sealed interface ScalingLazyListItemInfo {
+ method public float getAlpha();
+ method public int getIndex();
+ method public Object getKey();
+ method public int getOffset();
+ method public float getScale();
+ method public int getSize();
+ method public int getUnadjustedOffset();
+ method public int getUnadjustedSize();
+ property public abstract float alpha;
+ property public abstract int index;
+ property public abstract Object key;
+ property public abstract int offset;
+ property public abstract float scale;
+ property public abstract int size;
+ property public abstract int unadjustedOffset;
+ property public abstract int unadjustedSize;
+ }
+
+ @androidx.compose.runtime.Stable @androidx.wear.compose.foundation.lazy.ScalingLazyScopeMarker public sealed interface ScalingLazyListItemScope {
+ method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ }
+
+ public sealed interface ScalingLazyListLayoutInfo {
+ method public int getAfterAutoCenteringPadding();
+ method public int getAfterContentPadding();
+ method public int getAnchorType();
+ method public int getBeforeAutoCenteringPadding();
+ method public int getBeforeContentPadding();
+ method public androidx.compose.foundation.gestures.Orientation getOrientation();
+ method public boolean getReverseLayout();
+ method public int getTotalItemsCount();
+ method public int getViewportEndOffset();
+ method public long getViewportSize();
+ method public int getViewportStartOffset();
+ method public java.util.List<androidx.wear.compose.foundation.lazy.ScalingLazyListItemInfo> getVisibleItemsInfo();
+ property public abstract int afterAutoCenteringPadding;
+ property public abstract int afterContentPadding;
+ property public abstract int anchorType;
+ property public abstract int beforeAutoCenteringPadding;
+ property public abstract int beforeContentPadding;
+ property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+ property public abstract boolean reverseLayout;
+ property public abstract int totalItemsCount;
+ property public abstract int viewportEndOffset;
+ property public abstract long viewportSize;
+ property public abstract int viewportStartOffset;
+ property public abstract java.util.List<androidx.wear.compose.foundation.lazy.ScalingLazyListItemInfo> visibleItemsInfo;
+ }
+
+ @androidx.wear.compose.foundation.lazy.ScalingLazyScopeMarker public sealed interface ScalingLazyListScope {
+ method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListItemScope,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.wear.compose.foundation.lazy.ScalingLazyListItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ }
+
+ @androidx.compose.runtime.Stable public final class ScalingLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+ ctor public ScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
+ method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public float dispatchRawDelta(float delta);
+ method public int getCenterItemIndex();
+ method public int getCenterItemScrollOffset();
+ method public androidx.wear.compose.foundation.lazy.ScalingLazyListLayoutInfo getLayoutInfo();
+ method public boolean isScrollInProgress();
+ method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public boolean canScrollBackward;
+ property public boolean canScrollForward;
+ property public final int centerItemIndex;
+ property public final int centerItemScrollOffset;
+ property public boolean isScrollInProgress;
+ property public final androidx.wear.compose.foundation.lazy.ScalingLazyListLayoutInfo layoutInfo;
+ field public static final androidx.wear.compose.foundation.lazy.ScalingLazyListState.Companion Companion;
+ }
+
+ public static final class ScalingLazyListState.Companion {
+ method public androidx.compose.runtime.saveable.Saver<androidx.wear.compose.foundation.lazy.ScalingLazyListState,java.lang.Object> getSaver();
+ property public final androidx.compose.runtime.saveable.Saver<androidx.wear.compose.foundation.lazy.ScalingLazyListState,java.lang.Object> Saver;
+ }
+
+ public final class ScalingLazyListStateKt {
+ method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.lazy.ScalingLazyListState rememberScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
+ }
+
+ @kotlin.DslMarker public @interface ScalingLazyScopeMarker {
+ }
+
+ @androidx.compose.runtime.Stable public interface ScalingParams {
+ method public float getEdgeAlpha();
+ method public float getEdgeScale();
+ method public float getMaxElementHeight();
+ method public float getMaxTransitionArea();
+ method public float getMinElementHeight();
+ method public float getMinTransitionArea();
+ method public androidx.compose.animation.core.Easing getScaleInterpolator();
+ method public int resolveViewportVerticalOffset(long viewportConstraints);
+ property public abstract float edgeAlpha;
+ property public abstract float edgeScale;
+ property public abstract float maxElementHeight;
+ property public abstract float maxTransitionArea;
+ property public abstract float minElementHeight;
+ property public abstract float minTransitionArea;
+ property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
+ }
+
+}
+
diff --git a/wear/compose/compose-foundation/api/restricted_1.2.0-beta01.txt b/wear/compose/compose-foundation/api/restricted_1.2.0-beta01.txt
index 03d2707..91d6749 100644
--- a/wear/compose/compose-foundation/api/restricted_1.2.0-beta01.txt
+++ b/wear/compose/compose-foundation/api/restricted_1.2.0-beta01.txt
@@ -26,11 +26,6 @@
method public static void basicCurvedText(androidx.wear.compose.foundation.CurvedScope, String text, androidx.wear.compose.foundation.CurvedTextStyle style, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
}
- public final class CompositionLocalsKt {
- method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> getLocalReduceMotion();
- property @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.wear.compose.foundation.ReduceMotion> LocalReduceMotion;
- }
-
public interface CurvedAlignment {
}
@@ -200,61 +195,6 @@
property public final boolean expanded;
}
- @kotlin.RequiresOptIn(message="This Wear Foundation API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearFoundationApi {
- }
-
- public final class HierarchicalFocusCoordinatorKt {
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public fun interface ReduceMotion {
- method @androidx.compose.runtime.Composable public boolean enabled();
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public interface RevealScope {
- method public float getRevealOffset();
- property public abstract float revealOffset;
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public final class RevealState {
- method public suspend Object? animateTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public int getCurrentValue();
- method public float getOffset();
- method public java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> getSwipeAnchors();
- method public int getTargetValue();
- method public boolean isAnimationRunning();
- method public suspend Object? snapTo(int targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public final int currentValue;
- property public final boolean isAnimationRunning;
- property public final float offset;
- property public final java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> swipeAnchors;
- property public final int targetValue;
- }
-
- @androidx.wear.compose.foundation.ExperimentalWearFoundationApi @kotlin.jvm.JvmInline public final value class RevealValue {
- method public int getValue();
- property public final int value;
- field public static final androidx.wear.compose.foundation.RevealValue.Companion Companion;
- }
-
- public static final class RevealValue.Companion {
- method public int getCovered();
- method public int getRevealed();
- method public int getRevealing();
- property public final int Covered;
- property public final int Revealed;
- property public final int Revealing;
- }
-
- public final class SwipeToRevealKt {
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void SwipeToReveal(kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit> action, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onFullSwipe, optional androidx.wear.compose.foundation.RevealState state, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? additionalAction, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealScope,kotlin.Unit>? undoAction, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> createAnchors(optional float coveredAnchor, optional float revealingAnchor, optional float revealedAnchor);
- method @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.wear.compose.foundation.RevealState rememberRevealState(optional int initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.RevealValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold, optional java.util.Map<androidx.wear.compose.foundation.RevealValue,java.lang.Float> anchors);
- }
-
}
package androidx.wear.compose.foundation.lazy {
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/resources/robolectric.properties b/wear/protolayout/protolayout-expression-pipeline/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicInt32Test.java b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicInt32Test.java
index ff1bc0f..a8030ad 100644
--- a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicInt32Test.java
+++ b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/DynamicInt32Test.java
@@ -127,7 +127,7 @@
.toString())
.isEqualTo(
"Int32FormatOp{input=FixedInt32{value=1}, minIntegerDigits=2,"
- + " groupingUsed=true}");
+ + " groupingUsed=true}");
}
@Test
diff --git a/wear/protolayout/protolayout-expression/src/test/resources/robolectric.properties b/wear/protolayout/protolayout-expression/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/protolayout/protolayout-expression/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java
index 7e03861..54811fa 100644
--- a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java
@@ -73,6 +73,7 @@
return (int) ((px - 0.5f) / scale);
}
+ @SuppressWarnings("deprecation")
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java
index 0089ecd..f47f90d 100644
--- a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java
@@ -73,6 +73,7 @@
return (int) ((px - 0.5f) / scale);
}
+ @SuppressWarnings("deprecation")
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/wear/protolayout/protolayout-material/src/test/resources/robolectric.properties b/wear/protolayout/protolayout-material/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/protolayout/protolayout-renderer/src/test/resources/robolectric.properties b/wear/protolayout/protolayout-renderer/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/protolayout/protolayout-renderer/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/protolayout/protolayout/src/test/resources/robolectric.properties b/wear/protolayout/protolayout/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/protolayout/protolayout/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/MaterialGoldenXLTest.java b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/MaterialGoldenXLTest.java
index 0acea57..b30cd5a 100644
--- a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/MaterialGoldenXLTest.java
+++ b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/MaterialGoldenXLTest.java
@@ -74,6 +74,8 @@
}
@Parameterized.Parameters(name = "{0}")
+ // TODO(b/267744228): Remove the warning suppression.
+ @SuppressWarnings("deprecation")
public static Collection<Object[]> data() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
DisplayMetrics currentDisplayMetrics = new DisplayMetrics();
diff --git a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/LayoutsGoldenXLTest.java b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/LayoutsGoldenXLTest.java
index c4c4460..f449fcb 100644
--- a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/LayoutsGoldenXLTest.java
+++ b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/LayoutsGoldenXLTest.java
@@ -74,6 +74,8 @@
}
@Parameterized.Parameters(name = "{0}")
+ // TODO(b/267744228): Remove the warning suppression.
+ @SuppressWarnings("deprecation")
public static Collection<Object[]> data() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
DisplayMetrics currentDisplayMetrics = new DisplayMetrics();
diff --git a/wear/tiles/tiles-material/src/test/resources/robolectric.properties b/wear/tiles/tiles-material/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..34d9449
--- /dev/null
+++ b/wear/tiles/tiles-material/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
+sdk=29
diff --git a/wear/tiles/tiles-renderer/src/test/resources/robolectric.properties b/wear/tiles/tiles-renderer/src/test/resources/robolectric.properties
index 71111c5..69fde47 100644
--- a/wear/tiles/tiles-renderer/src/test/resources/robolectric.properties
+++ b/wear/tiles/tiles-renderer/src/test/resources/robolectric.properties
@@ -1,17 +1,3 @@
-#
-# 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.
-#
-
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/tiles/tiles-testing/src/test/resources/robolectric.properties b/wear/tiles/tiles-testing/src/test/resources/robolectric.properties
index 27d4acf..69fde47 100644
--- a/wear/tiles/tiles-testing/src/test/resources/robolectric.properties
+++ b/wear/tiles/tiles-testing/src/test/resources/robolectric.properties
@@ -1,18 +1,3 @@
-#
-# 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.
-#
-
# robolectric properties
-
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/tiles/tiles/src/test/resources/robolectric.properties b/wear/tiles/tiles/src/test/resources/robolectric.properties
index 27d4acf..69fde47 100644
--- a/wear/tiles/tiles/src/test/resources/robolectric.properties
+++ b/wear/tiles/tiles/src/test/resources/robolectric.properties
@@ -1,18 +1,3 @@
-#
-# 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.
-#
-
# robolectric properties
-
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-client/src/test/resources/robolectric.properties b/wear/watchface/watchface-client/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/wear/watchface/watchface-client/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-client/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-complications-data-source-ktx/src/test/resources/robolectric.properties b/wear/watchface/watchface-complications-data-source-ktx/src/test/resources/robolectric.properties
index 4b7155e..69fde47 100644
--- a/wear/watchface/watchface-complications-data-source-ktx/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-complications-data-source-ktx/src/test/resources/robolectric.properties
@@ -1,17 +1,3 @@
-#
-# 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.
-#
-
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-complications-data-source/src/test/resources/robolectric.properties b/wear/watchface/watchface-complications-data-source/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-complications-data-source/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-complications-data/src/test/resources/robolectric.properties b/wear/watchface/watchface-complications-data/src/test/resources/robolectric.properties
index ce87047..69fde47 100644
--- a/wear/watchface/watchface-complications-data/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-complications-data/src/test/resources/robolectric.properties
@@ -1,3 +1,3 @@
-# Robolectric currently doesn't support API 30, so we have to explicitly specify 29 as the target
-# sdk for now. Remove when no longer necessary.
-sdk=29
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-complications-rendering/src/test/resources/robolectric.properties b/wear/watchface/watchface-complications-rendering/src/test/resources/robolectric.properties
index ce87047..69fde47 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-complications-rendering/src/test/resources/robolectric.properties
@@ -1,3 +1,3 @@
-# Robolectric currently doesn't support API 30, so we have to explicitly specify 29 as the target
-# sdk for now. Remove when no longer necessary.
-sdk=29
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-complications/src/test/resources/robolectric.properties b/wear/watchface/watchface-complications/src/test/resources/robolectric.properties
index ce87047..34d9449 100644
--- a/wear/watchface/watchface-complications/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-complications/src/test/resources/robolectric.properties
@@ -1,3 +1,3 @@
-# Robolectric currently doesn't support API 30, so we have to explicitly specify 29 as the target
-# sdk for now. Remove when no longer necessary.
+# robolectric properties
+# Temporary until Wear team fixes their tests to work against sdk=33 (b/281072091).
sdk=29
diff --git a/wear/watchface/watchface-guava/src/test/resources/robolectric.properties b/wear/watchface/watchface-guava/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/wear/watchface/watchface-guava/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-guava/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface-style/src/test/resources/robolectric.properties b/wear/watchface/watchface-style/src/test/resources/robolectric.properties
index 80e2a6f..69fde47 100644
--- a/wear/watchface/watchface-style/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface-style/src/test/resources/robolectric.properties
@@ -1 +1,3 @@
# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/watchface/watchface/src/test/resources/robolectric.properties b/wear/watchface/watchface/src/test/resources/robolectric.properties
index ce87047..69fde47 100644
--- a/wear/watchface/watchface/src/test/resources/robolectric.properties
+++ b/wear/watchface/watchface/src/test/resources/robolectric.properties
@@ -1,3 +1,3 @@
-# Robolectric currently doesn't support API 30, so we have to explicitly specify 29 as the target
-# sdk for now. Remove when no longer necessary.
-sdk=29
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/wear-input/src/test/resources/robolectric.properties b/wear/wear-input/src/test/resources/robolectric.properties
index 4b7155e..69fde47 100644
--- a/wear/wear-input/src/test/resources/robolectric.properties
+++ b/wear/wear-input/src/test/resources/robolectric.properties
@@ -1,17 +1,3 @@
-#
-# 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.
-#
-
-# robolectric properties
\ No newline at end of file
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/RemoteAuthClient.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/RemoteAuthClient.kt
index 23cfd65..c564a62 100644
--- a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/RemoteAuthClient.kt
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/authentication/RemoteAuthClient.kt
@@ -161,11 +161,11 @@
return RemoteAuthClient(
object : ServiceBinder {
override fun bindService(
- intent: Intent?,
- connection: ServiceConnection?,
+ intent: Intent,
+ connection: ServiceConnection,
flags: Int
): Boolean {
- return appContext.bindService(intent, connection!!, flags)
+ return appContext.bindService(intent, connection, flags)
}
override fun unbindService(connection: ServiceConnection?) {
@@ -276,7 +276,7 @@
internal interface ServiceBinder {
/** See [Context.bindService]. */
- fun bindService(intent: Intent?, connection: ServiceConnection?, flags: Int): Boolean
+ fun bindService(intent: Intent, connection: ServiceConnection, flags: Int): Boolean
/** See [Context.unbindService]. */
fun unbindService(connection: ServiceConnection?)
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
index 1f175a9..f53fb50 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/authentication/RemoteAuthTest.kt
@@ -209,11 +209,11 @@
var state = ConnectionState.DISCONNECTED
private var serviceConnection: ServiceConnection? = null
override fun bindService(
- intent: Intent?,
- connection: ServiceConnection?,
+ intent: Intent,
+ connection: ServiceConnection,
flags: Int
): Boolean {
- if (intent!!.getPackage() != RemoteAuthClient.WEARABLE_PACKAGE_NAME) {
+ if (intent.getPackage() != RemoteAuthClient.WEARABLE_PACKAGE_NAME) {
throw UnsupportedOperationException()
}
if (intent.action != RemoteAuthClient.ACTION_AUTH) {
diff --git a/wear/wear/src/test/resources/robolectric.properties b/wear/wear/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/wear/wear/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/webkit/webkit/api/1.6.0-beta02.txt b/webkit/webkit/api/1.6.0-beta02.txt
deleted file mode 100644
index faf13cb..0000000
--- a/webkit/webkit/api/1.6.0-beta02.txt
+++ /dev/null
@@ -1,300 +0,0 @@
-// Signature format: 4.0
-package androidx.webkit {
-
- public class CookieManagerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
- }
-
- public abstract class JavaScriptReplyProxy {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
- }
-
- public class ProcessGlobalConfig {
- ctor public ProcessGlobalConfig();
- method public static void apply(androidx.webkit.ProcessGlobalConfig);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
- }
-
- public final class ProxyConfig {
- method public java.util.List<java.lang.String!> getBypassRules();
- method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
- method public boolean isReverseBypassEnabled();
- field public static final String MATCH_ALL_SCHEMES = "*";
- field public static final String MATCH_HTTP = "http";
- field public static final String MATCH_HTTPS = "https";
- }
-
- public static final class ProxyConfig.Builder {
- ctor public ProxyConfig.Builder();
- ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
- method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect();
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
- method public androidx.webkit.ProxyConfig build();
- method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
- method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
- }
-
- public static final class ProxyConfig.ProxyRule {
- method public String getSchemeFilter();
- method public String getUrl();
- }
-
- public abstract class ProxyController {
- method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
- method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
- }
-
- public abstract class SafeBrowsingResponseCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
- }
-
- public abstract class ServiceWorkerClientCompat {
- ctor public ServiceWorkerClientCompat();
- method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
- }
-
- public abstract class ServiceWorkerControllerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
- method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
- method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
- }
-
- public abstract class ServiceWorkerWebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
- }
-
- public class TracingConfig {
- method public java.util.List<java.lang.String!> getCustomIncludedCategories();
- method public int getPredefinedCategories();
- method public int getTracingMode();
- field public static final int CATEGORIES_ALL = 1; // 0x1
- field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
- field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
- field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
- field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
- field public static final int CATEGORIES_NONE = 0; // 0x0
- field public static final int CATEGORIES_RENDERING = 16; // 0x10
- field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
- field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
- field public static final int RECORD_UNTIL_FULL = 0; // 0x0
- }
-
- public static class TracingConfig.Builder {
- ctor public TracingConfig.Builder();
- method public androidx.webkit.TracingConfig.Builder addCategories(int...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
- method public androidx.webkit.TracingConfig build();
- method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
- }
-
- public abstract class TracingController {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
- method public abstract boolean isTracing();
- method public abstract void start(androidx.webkit.TracingConfig);
- method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
- }
-
- public class WebMessageCompat {
- ctor public WebMessageCompat(String?);
- ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
- method public String? getData();
- method public androidx.webkit.WebMessagePortCompat![]? getPorts();
- }
-
- public abstract class WebMessagePortCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- }
-
- public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
- ctor public WebMessagePortCompat.WebMessageCallbackCompat();
- method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
- }
-
- public abstract class WebResourceErrorCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
- }
-
- public class WebResourceRequestCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
- }
-
- public class WebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
- field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
- field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
- field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
- field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
- }
-
- public final class WebViewAssetLoader {
- method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
- field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
- }
-
- public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.Builder {
- ctor public WebViewAssetLoader.Builder();
- method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
- method public androidx.webkit.WebViewAssetLoader build();
- method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
- method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
- }
-
- public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
- method @WorkerThread public android.webkit.WebResourceResponse handle(String);
- }
-
- public static interface WebViewAssetLoader.PathHandler {
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public class WebViewClientCompat extends android.webkit.WebViewClient {
- ctor public WebViewClientCompat();
- method @RequiresApi(23) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
- method @RequiresApi(21) @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
- method @RequiresApi(27) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
- method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
- }
-
- public class WebViewCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
- method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
- }
-
- public static interface WebViewCompat.VisualStateCallback {
- method @UiThread public void onComplete(long);
- }
-
- public static interface WebViewCompat.WebMessageListener {
- method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
- }
-
- public class WebViewFeature {
- method public static boolean isFeatureSupported(String);
- method public static boolean isStartupFeatureSupported(android.content.Context, String);
- field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
- field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
- field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
- field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
- field public static final String FORCE_DARK = "FORCE_DARK";
- field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
- field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
- field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
- field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
- field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
- field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
- field public static final String MULTI_PROCESS = "MULTI_PROCESS";
- field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
- field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
- field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
- field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
- field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
- field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
- field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
- field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
- field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
- field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
- field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
- field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
- field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
- field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
- field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
- field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
- field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
- field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
- field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
- field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
- field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
- field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
- field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
- field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
- field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
- field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
- field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
- field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
- field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
- field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
- field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
- field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
- field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
- field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
- field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
- }
-
- public abstract class WebViewRenderProcess {
- ctor public WebViewRenderProcess();
- method public abstract boolean terminate();
- }
-
- public abstract class WebViewRenderProcessClient {
- ctor public WebViewRenderProcessClient();
- method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- }
-
-}
-
diff --git a/webkit/webkit/api/public_plus_experimental_1.6.0-beta02.txt b/webkit/webkit/api/public_plus_experimental_1.6.0-beta02.txt
deleted file mode 100644
index faf13cb..0000000
--- a/webkit/webkit/api/public_plus_experimental_1.6.0-beta02.txt
+++ /dev/null
@@ -1,300 +0,0 @@
-// Signature format: 4.0
-package androidx.webkit {
-
- public class CookieManagerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
- }
-
- public abstract class JavaScriptReplyProxy {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
- }
-
- public class ProcessGlobalConfig {
- ctor public ProcessGlobalConfig();
- method public static void apply(androidx.webkit.ProcessGlobalConfig);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
- }
-
- public final class ProxyConfig {
- method public java.util.List<java.lang.String!> getBypassRules();
- method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
- method public boolean isReverseBypassEnabled();
- field public static final String MATCH_ALL_SCHEMES = "*";
- field public static final String MATCH_HTTP = "http";
- field public static final String MATCH_HTTPS = "https";
- }
-
- public static final class ProxyConfig.Builder {
- ctor public ProxyConfig.Builder();
- ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
- method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect();
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
- method public androidx.webkit.ProxyConfig build();
- method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
- method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
- }
-
- public static final class ProxyConfig.ProxyRule {
- method public String getSchemeFilter();
- method public String getUrl();
- }
-
- public abstract class ProxyController {
- method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
- method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
- }
-
- public abstract class SafeBrowsingResponseCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
- }
-
- public abstract class ServiceWorkerClientCompat {
- ctor public ServiceWorkerClientCompat();
- method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
- }
-
- public abstract class ServiceWorkerControllerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
- method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
- method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
- }
-
- public abstract class ServiceWorkerWebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
- }
-
- public class TracingConfig {
- method public java.util.List<java.lang.String!> getCustomIncludedCategories();
- method public int getPredefinedCategories();
- method public int getTracingMode();
- field public static final int CATEGORIES_ALL = 1; // 0x1
- field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
- field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
- field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
- field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
- field public static final int CATEGORIES_NONE = 0; // 0x0
- field public static final int CATEGORIES_RENDERING = 16; // 0x10
- field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
- field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
- field public static final int RECORD_UNTIL_FULL = 0; // 0x0
- }
-
- public static class TracingConfig.Builder {
- ctor public TracingConfig.Builder();
- method public androidx.webkit.TracingConfig.Builder addCategories(int...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
- method public androidx.webkit.TracingConfig build();
- method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
- }
-
- public abstract class TracingController {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
- method public abstract boolean isTracing();
- method public abstract void start(androidx.webkit.TracingConfig);
- method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
- }
-
- public class WebMessageCompat {
- ctor public WebMessageCompat(String?);
- ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
- method public String? getData();
- method public androidx.webkit.WebMessagePortCompat![]? getPorts();
- }
-
- public abstract class WebMessagePortCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- }
-
- public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
- ctor public WebMessagePortCompat.WebMessageCallbackCompat();
- method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
- }
-
- public abstract class WebResourceErrorCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
- }
-
- public class WebResourceRequestCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
- }
-
- public class WebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
- field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
- field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
- field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
- field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
- }
-
- public final class WebViewAssetLoader {
- method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
- field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
- }
-
- public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.Builder {
- ctor public WebViewAssetLoader.Builder();
- method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
- method public androidx.webkit.WebViewAssetLoader build();
- method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
- method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
- }
-
- public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
- method @WorkerThread public android.webkit.WebResourceResponse handle(String);
- }
-
- public static interface WebViewAssetLoader.PathHandler {
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public class WebViewClientCompat extends android.webkit.WebViewClient {
- ctor public WebViewClientCompat();
- method @RequiresApi(23) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
- method @RequiresApi(21) @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
- method @RequiresApi(27) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
- method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
- }
-
- public class WebViewCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
- method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
- }
-
- public static interface WebViewCompat.VisualStateCallback {
- method @UiThread public void onComplete(long);
- }
-
- public static interface WebViewCompat.WebMessageListener {
- method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
- }
-
- public class WebViewFeature {
- method public static boolean isFeatureSupported(String);
- method public static boolean isStartupFeatureSupported(android.content.Context, String);
- field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
- field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
- field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
- field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
- field public static final String FORCE_DARK = "FORCE_DARK";
- field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
- field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
- field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
- field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
- field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
- field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
- field public static final String MULTI_PROCESS = "MULTI_PROCESS";
- field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
- field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
- field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
- field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
- field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
- field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
- field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
- field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
- field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
- field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
- field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
- field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
- field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
- field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
- field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
- field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
- field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
- field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
- field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
- field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
- field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
- field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
- field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
- field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
- field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
- field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
- field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
- field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
- field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
- field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
- field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
- field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
- field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
- field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
- field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
- }
-
- public abstract class WebViewRenderProcess {
- ctor public WebViewRenderProcess();
- method public abstract boolean terminate();
- }
-
- public abstract class WebViewRenderProcessClient {
- ctor public WebViewRenderProcessClient();
- method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- }
-
-}
-
diff --git a/webkit/webkit/api/restricted_1.6.0-beta02.txt b/webkit/webkit/api/restricted_1.6.0-beta02.txt
deleted file mode 100644
index faf13cb..0000000
--- a/webkit/webkit/api/restricted_1.6.0-beta02.txt
+++ /dev/null
@@ -1,300 +0,0 @@
-// Signature format: 4.0
-package androidx.webkit {
-
- public class CookieManagerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
- }
-
- public abstract class JavaScriptReplyProxy {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
- }
-
- public class ProcessGlobalConfig {
- ctor public ProcessGlobalConfig();
- method public static void apply(androidx.webkit.ProcessGlobalConfig);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
- }
-
- public final class ProxyConfig {
- method public java.util.List<java.lang.String!> getBypassRules();
- method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
- method public boolean isReverseBypassEnabled();
- field public static final String MATCH_ALL_SCHEMES = "*";
- field public static final String MATCH_HTTP = "http";
- field public static final String MATCH_HTTPS = "https";
- }
-
- public static final class ProxyConfig.Builder {
- ctor public ProxyConfig.Builder();
- ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
- method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect(String);
- method public androidx.webkit.ProxyConfig.Builder addDirect();
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
- method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
- method public androidx.webkit.ProxyConfig build();
- method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
- method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
- }
-
- public static final class ProxyConfig.ProxyRule {
- method public String getSchemeFilter();
- method public String getUrl();
- }
-
- public abstract class ProxyController {
- method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
- method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
- }
-
- public abstract class SafeBrowsingResponseCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
- }
-
- public abstract class ServiceWorkerClientCompat {
- ctor public ServiceWorkerClientCompat();
- method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
- }
-
- public abstract class ServiceWorkerControllerCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
- method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
- method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
- }
-
- public abstract class ServiceWorkerWebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
- }
-
- public class TracingConfig {
- method public java.util.List<java.lang.String!> getCustomIncludedCategories();
- method public int getPredefinedCategories();
- method public int getTracingMode();
- field public static final int CATEGORIES_ALL = 1; // 0x1
- field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
- field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
- field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
- field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
- field public static final int CATEGORIES_NONE = 0; // 0x0
- field public static final int CATEGORIES_RENDERING = 16; // 0x10
- field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
- field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
- field public static final int RECORD_UNTIL_FULL = 0; // 0x0
- }
-
- public static class TracingConfig.Builder {
- ctor public TracingConfig.Builder();
- method public androidx.webkit.TracingConfig.Builder addCategories(int...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
- method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
- method public androidx.webkit.TracingConfig build();
- method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
- }
-
- public abstract class TracingController {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
- method public abstract boolean isTracing();
- method public abstract void start(androidx.webkit.TracingConfig);
- method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
- }
-
- public class WebMessageCompat {
- ctor public WebMessageCompat(String?);
- ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
- method public String? getData();
- method public androidx.webkit.WebMessagePortCompat![]? getPorts();
- }
-
- public abstract class WebMessagePortCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
- }
-
- public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
- ctor public WebMessagePortCompat.WebMessageCallbackCompat();
- method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
- }
-
- public abstract class WebResourceErrorCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
- }
-
- public class WebResourceRequestCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
- }
-
- public class WebSettingsCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
- method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
- field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
- field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
- field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
- field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
- field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
- }
-
- public final class WebViewAssetLoader {
- method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
- field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
- }
-
- public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.Builder {
- ctor public WebViewAssetLoader.Builder();
- method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
- method public androidx.webkit.WebViewAssetLoader build();
- method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
- method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
- }
-
- public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
- method @WorkerThread public android.webkit.WebResourceResponse handle(String);
- }
-
- public static interface WebViewAssetLoader.PathHandler {
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
- ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
- method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
- }
-
- public class WebViewClientCompat extends android.webkit.WebViewClient {
- ctor public WebViewClientCompat();
- method @RequiresApi(23) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
- method @RequiresApi(21) @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
- method @RequiresApi(27) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
- method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
- }
-
- public class WebViewCompat {
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
- method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
- method @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
- }
-
- public static interface WebViewCompat.VisualStateCallback {
- method @UiThread public void onComplete(long);
- }
-
- public static interface WebViewCompat.WebMessageListener {
- method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
- }
-
- public class WebViewFeature {
- method public static boolean isFeatureSupported(String);
- method public static boolean isStartupFeatureSupported(android.content.Context, String);
- field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
- field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
- field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
- field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
- field public static final String FORCE_DARK = "FORCE_DARK";
- field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
- field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
- field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
- field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
- field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
- field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
- field public static final String MULTI_PROCESS = "MULTI_PROCESS";
- field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
- field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
- field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
- field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
- field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
- field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
- field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
- field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
- field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
- field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
- field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
- field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
- field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
- field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
- field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
- field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
- field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
- field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
- field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
- field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
- field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
- field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
- field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
- field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
- field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
- field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
- field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
- field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
- field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
- field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
- field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
- field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
- field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
- field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
- field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
- }
-
- public abstract class WebViewRenderProcess {
- ctor public WebViewRenderProcess();
- method public abstract boolean terminate();
- }
-
- public abstract class WebViewRenderProcessClient {
- ctor public WebViewRenderProcessClient();
- method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
- }
-
-}
-
diff --git a/window/extensions/extensions/api/current.txt b/window/extensions/extensions/api/current.txt
index 20f4ee8..22888f8 100644
--- a/window/extensions/extensions/api/current.txt
+++ b/window/extensions/extensions/api/current.txt
@@ -53,11 +53,15 @@
public interface ActivityEmbeddingComponent {
method public void clearSplitAttributesCalculator();
method public void clearSplitInfoCallback();
+ method public default void finishActivityStacks(java.util.Set<android.os.IBinder!>);
+ method public default void invalidateTopVisibleSplitAttributes();
method public boolean isActivityEmbedded(android.app.Activity);
method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
+ method public default android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.os.IBinder);
method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
method public default void setSplitInfoCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+ method public default void updateSplitAttributes(android.os.IBinder, androidx.window.extensions.embedding.SplitAttributes);
}
public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
@@ -135,6 +139,7 @@
method public androidx.window.extensions.embedding.ActivityStack getSecondaryActivityStack();
method public androidx.window.extensions.embedding.SplitAttributes getSplitAttributes();
method @Deprecated public float getSplitRatio();
+ method public android.os.IBinder getToken();
}
public class SplitPairRule extends androidx.window.extensions.embedding.SplitRule {
diff --git a/window/extensions/extensions/api/restricted_current.txt b/window/extensions/extensions/api/restricted_current.txt
index 20f4ee8..22888f8 100644
--- a/window/extensions/extensions/api/restricted_current.txt
+++ b/window/extensions/extensions/api/restricted_current.txt
@@ -53,11 +53,15 @@
public interface ActivityEmbeddingComponent {
method public void clearSplitAttributesCalculator();
method public void clearSplitInfoCallback();
+ method public default void finishActivityStacks(java.util.Set<android.os.IBinder!>);
+ method public default void invalidateTopVisibleSplitAttributes();
method public boolean isActivityEmbedded(android.app.Activity);
method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
+ method public default android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.os.IBinder);
method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
method public default void setSplitInfoCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+ method public default void updateSplitAttributes(android.os.IBinder, androidx.window.extensions.embedding.SplitAttributes);
}
public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
@@ -135,6 +139,7 @@
method public androidx.window.extensions.embedding.ActivityStack getSecondaryActivityStack();
method public androidx.window.extensions.embedding.SplitAttributes getSplitAttributes();
method @Deprecated public float getSplitRatio();
+ method public android.os.IBinder getToken();
}
public class SplitPairRule extends androidx.window.extensions.embedding.SplitRule {
diff --git a/window/extensions/extensions/build.gradle b/window/extensions/extensions/build.gradle
index 4941b4f2..ef58960 100644
--- a/window/extensions/extensions/build.gradle
+++ b/window/extensions/extensions/build.gradle
@@ -26,7 +26,7 @@
api(libs.kotlinStdlib)
implementation("androidx.annotation:annotation:1.6.0")
implementation("androidx.annotation:annotation-experimental:1.1.0")
- implementation("androidx.window.extensions.core:core:1.0.0-rc01")
+ implementation("androidx.window.extensions.core:core:1.0.0-beta01")
testImplementation(libs.robolectric)
testImplementation(libs.testExtJunit)
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
index e34b0e5..d50e6b3 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
@@ -18,12 +18,20 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.app.ActivityOptions;
+import android.os.IBinder;
+
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.window.extensions.area.WindowAreaComponent;
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.ActivityStack;
+import androidx.window.extensions.embedding.SplitAttributes;
+import androidx.window.extensions.embedding.SplitInfo;
import androidx.window.extensions.layout.WindowLayoutComponent;
+import java.util.Set;
+
/**
* A class to provide instances of different WindowManager Jetpack extension components. An OEM must
* implement all the availability methods to state which WindowManager Jetpack extension
@@ -55,6 +63,7 @@
* <li>{@link androidx.window.extensions.layout.FoldingFeature} APIs</li>
* <li>{@link androidx.window.extensions.layout.WindowLayoutInfo} APIs</li>
* <li>{@link androidx.window.extensions.layout.WindowLayoutComponent} APIs</li>
+ * <li>{@link androidx.window.extensions.area.WindowAreaComponent} APIs</li>
* </ul>
* </p>
*/
@@ -76,6 +85,27 @@
@RestrictTo(LIBRARY_GROUP)
int VENDOR_API_LEVEL_2 = 2;
+ // TODO(b/241323716) Removed after we have annotation to check API level
+ /**
+ * A vendor API level constant. It helps to unify the format of documenting {@code @since}
+ * block.
+ * <p>
+ * The added APIs for Vendor API level 3 are:
+ * <ul>
+ * <li>{@link ActivityStack#getToken()}</li>
+ * <li>{@link SplitInfo#getToken()}</li>
+ * <li>{@link ActivityEmbeddingComponent#setLaunchingActivityStack(ActivityOptions,
+ * IBinder)}</li>
+ * <li>{@link ActivityEmbeddingComponent#invalidateTopVisibleSplitAttributes()}</li>
+ * <li>{@link ActivityEmbeddingComponent#updateSplitAttributes(IBinder, SplitAttributes)}
+ * </li>
+ * <li>{@link ActivityEmbeddingComponent#finishActivityStacks(Set)}</li>
+ * </ul>
+ * </p>
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ int VENDOR_API_LEVEL_3 = 3;
+
/**
* Returns the API level of the vendor library on the device. If the returned version is not
* supported by the WindowManager library, then some functions may not be available or replaced
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
index ddb8e8a..0ce24b8 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
@@ -25,7 +25,7 @@
* An interface representing a container in an extension window area in which app content can be
* shown.
*
- * @since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
* @see WindowAreaComponent#getRearDisplayPresentation()
*/
public interface ExtensionWindowAreaPresentation {
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
index 2f521bc..0dcd47f 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
@@ -23,7 +23,7 @@
/**
* Interface to provide information around the current status of a window area feature.
*
- * @since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
* @see WindowAreaComponent#addRearDisplayPresentationStatusListener
*/
public interface ExtensionWindowAreaStatus {
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
index e20584c..2fbbe20 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -16,12 +16,16 @@
package androidx.window.extensions.area;
+import android.annotation.SuppressLint;
import android.app.Activity;
+import android.os.Build;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.window.extensions.WindowExtensions;
import androidx.window.extensions.core.util.function.Consumer;
@@ -42,6 +46,7 @@
* @see WindowExtensions#getWindowLayoutComponent()
*/
public interface WindowAreaComponent {
+
/**
* WindowArea status constant to signify that the feature is
* unsupported on this device. Could be due to the device not supporting that
@@ -120,6 +125,13 @@
})
@interface WindowAreaSessionState {}
+ // TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to
+ // the latest library.
+ @SuppressLint({"NewApi", "ClassVerificationFailure"})
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ ArrayMap<java.util.function.Consumer<Integer>, Consumer<Integer>> JAVA_TO_EXTENSIONS_MAP =
+ new ArrayMap<>();
+
/**
* Adds a listener interested in receiving updates on the RearDisplayStatus
* of the device. Because this is being called from the OEM provided
@@ -134,6 +146,27 @@
*/
void addRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+ // TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to
+ // the latest library.
+ /**
+ * @deprecated Use {@link #addRearDisplayStatusListener(Consumer)}.
+ *
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+ */
+ @Deprecated
+ @SuppressLint("ClassVerificationFailure")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ default void addRearDisplayStatusListener(
+ @NonNull java.util.function.Consumer<Integer> consumer) {
+ if (JAVA_TO_EXTENSIONS_MAP.containsKey(consumer)) {
+ return;
+ }
+ final Consumer<Integer> extensionsConsumer = consumer::accept;
+ JAVA_TO_EXTENSIONS_MAP.put(consumer, extensionsConsumer);
+ addRearDisplayStatusListener(extensionsConsumer);
+ }
+
/**
* Removes a listener no longer interested in receiving updates.
* @param consumer no longer interested in receiving updates to WindowAreaStatus
@@ -141,6 +174,26 @@
*/
void removeRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+ // TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to
+ // the latest library.
+ /**
+ * @deprecated Use {@link #removeRearDisplayStatusListener(Consumer)}.
+ *
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+ */
+ @Deprecated
+ @SuppressLint("ClassVerificationFailure")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ default void removeRearDisplayStatusListener(
+ @NonNull java.util.function.Consumer<Integer> consumer) {
+ if (!JAVA_TO_EXTENSIONS_MAP.containsKey(consumer)) {
+ return;
+ }
+ final Consumer<Integer> extensionsConsumer = JAVA_TO_EXTENSIONS_MAP.remove(consumer);
+ removeRearDisplayStatusListener(extensionsConsumer);
+ }
+
/**
* Creates and starts a rear display session and sends state updates to the
* consumer provided. This consumer will receive a constant represented by
@@ -164,6 +217,23 @@
void startRearDisplaySession(@NonNull Activity activity,
@NonNull Consumer<@WindowAreaSessionState Integer> consumer);
+ // TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to
+ // the latest library.
+ /**
+ * @deprecated Use {@link #startRearDisplaySession(Activity, Consumer)}.
+ *
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+ */
+ @Deprecated
+ @SuppressLint("ClassVerificationFailure")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ default void startRearDisplaySession(@NonNull Activity activity,
+ @NonNull java.util.function.Consumer<@WindowAreaSessionState Integer> consumer) {
+ final Consumer<Integer> extensionsConsumer = consumer::accept;
+ startRearDisplaySession(activity, extensionsConsumer);
+ }
+
/**
* Ends a RearDisplaySession and sends [STATE_INACTIVE] to the consumer
* provided in the {@code startRearDisplaySession} method. This method is only
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
index bdca977..121520c 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
@@ -17,6 +17,8 @@
package androidx.window.extensions.embedding;
import android.app.Activity;
+import android.app.ActivityOptions;
+import android.os.IBinder;
import android.view.WindowMetrics;
import androidx.annotation.NonNull;
@@ -124,4 +126,60 @@
* Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
*/
void clearSplitAttributesCalculator();
+
+ /**
+ * Sets the launching {@link ActivityStack} to the given {@link ActivityOptions}.
+ *
+ * @param options The {@link ActivityOptions} to be updated.
+ * @param token The {@link ActivityStack#getToken()} to represent the {@link ActivityStack}
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ @NonNull
+ default ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
+ @NonNull IBinder token) {
+ throw new UnsupportedOperationException("This method must not be called unless there is a"
+ + " corresponding override implementation on the device.");
+ }
+
+ /**
+ * Finishes a set of {@link ActivityStack}s. When an {@link ActivityStack} that was in an active
+ * split is finished, the other {@link ActivityStack} in the same {@link SplitInfo} can be
+ * expanded to fill the parent task container.
+ *
+ * @param activityStackTokens The set of tokens of {@link ActivityStack}-s that is going to be
+ * finished.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ default void finishActivityStacks(@NonNull Set<IBinder> activityStackTokens) {
+ throw new UnsupportedOperationException("This method must not be called unless there is a"
+ + " corresponding override implementation on the device.");
+ }
+
+ /**
+ * Triggers an update of the split attributes for the top split if there is one visible by
+ * making extensions invoke the split attributes calculator callback. This method can be used
+ * when a change to the split presentation originates from the application state change rather
+ * than driven by parent window changes or new activity starts. The call will be ignored if
+ * there is no visible split.
+ * @see #setSplitAttributesCalculator(Function)
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ default void invalidateTopVisibleSplitAttributes() {
+ throw new UnsupportedOperationException("This method must not be called unless there is a"
+ + " corresponding override implementation on the device.");
+ }
+
+ /**
+ * Updates the {@link SplitAttributes} of a split pair. This is an alternative to using
+ * a split attributes calculator callback, applicable when apps only need to update the
+ * splits in a few cases but rely on the default split attributes otherwise.
+ * @param splitInfoToken The identifier of the split pair to update.
+ * @param splitAttributes The {@link SplitAttributes} to apply to the split pair.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ default void updateSplitAttributes(@NonNull IBinder splitInfoToken,
+ @NonNull SplitAttributes splitAttributes) {
+ throw new UnsupportedOperationException("This method must not be called unless there is a"
+ + " corresponding override implementation on the device.");
+ }
}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
index 857738d..e568666 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
@@ -17,8 +17,12 @@
package androidx.window.extensions.embedding;
import android.app.Activity;
+import android.os.Binder;
+import android.os.IBinder;
import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.WindowExtensions;
import java.util.ArrayList;
import java.util.List;
@@ -30,11 +34,17 @@
*/
public class ActivityStack {
+ /** Only used for compatibility with the deprecated constructor. */
+ private static final IBinder INVALID_ACTIVITY_STACK_TOKEN = new Binder();
+
@NonNull
private final List<Activity> mActivities;
private final boolean mIsEmpty;
+ @NonNull
+ private final IBinder mToken;
+
/**
* The {@code ActivityStack} constructor
*
@@ -42,11 +52,24 @@
* belongs to this {@code ActivityStack}
* @param isEmpty Indicates whether there's any {@link Activity} running in this
* {@code ActivityStack}
+ * @param token The token to identify this {@code ActivityStack}
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
*/
- ActivityStack(@NonNull List<Activity> activities, boolean isEmpty) {
+ ActivityStack(@NonNull List<Activity> activities, boolean isEmpty, @NonNull IBinder token) {
Objects.requireNonNull(activities);
+ Objects.requireNonNull(token);
mActivities = new ArrayList<>(activities);
mIsEmpty = isEmpty;
+ mToken = token;
+ }
+
+ /**
+ * @deprecated Use the {@link WindowExtensions#VENDOR_API_LEVEL_3} version.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+ */
+ @Deprecated
+ ActivityStack(@NonNull List<Activity> activities, boolean isEmpty) {
+ this(activities, isEmpty, INVALID_ACTIVITY_STACK_TOKEN);
}
/**
@@ -76,19 +99,31 @@
return mIsEmpty;
}
+ /**
+ * Returns a token uniquely identifying the container.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
+ public IBinder getToken() {
+ return mToken;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ActivityStack)) return false;
ActivityStack that = (ActivityStack) o;
return mActivities.equals(that.mActivities)
- && mIsEmpty == that.mIsEmpty;
+ && mIsEmpty == that.mIsEmpty
+ && mToken.equals(that.mToken);
}
@Override
public int hashCode() {
int result = (mIsEmpty ? 1 : 0);
result = result * 31 + mActivities.hashCode();
+ result = result * 31 + mToken.hashCode();
return result;
}
@@ -97,6 +132,7 @@
public String toString() {
return "ActivityStack{" + "mActivities=" + mActivities
+ ", mIsEmpty=" + mIsEmpty
+ + ", mToken=" + mToken
+ '}';
}
}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculator.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculator.java
new file mode 100644
index 0000000..8819386
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculator.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.content.res.Configuration;
+import android.os.Build;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.layout.WindowLayoutInfo;
+
+// TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to the
+// latest library.
+/**
+ * @deprecated Use {@link androidx.window.extensions.core.util.function.Function} instead unless
+ * {@link androidx.window.extensions.core.util.function.Function} cannot be used.
+ */
+@Deprecated
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface SplitAttributesCalculator {
+ /** @deprecated See {@link SplitAttributesCalculator}. */
+ @Deprecated
+ @NonNull
+ SplitAttributes computeSplitAttributesForParams(
+ @NonNull SplitAttributesCalculatorParams params);
+
+ /**
+ * @deprecated Use
+ * {@link androidx.window.extensions.embedding.SplitAttributesCalculatorParams} unless
+ * {@link androidx.window.extensions.embedding.SplitAttributesCalculatorParams} cannot be used.
+ */
+ @Deprecated
+ class SplitAttributesCalculatorParams {
+ @NonNull
+ private final WindowMetrics mParentWindowMetrics;
+ @NonNull
+ private final Configuration mParentConfiguration;
+ @NonNull
+ private final WindowLayoutInfo mParentWindowLayoutInfo;
+ @NonNull
+ private final SplitAttributes mDefaultSplitAttributes;
+ private final boolean mIsDefaultMinSizeSatisfied;
+ @Nullable
+ private final String mSplitRuleTag;
+
+ /** Returns the parent container's {@link WindowMetrics} */
+ @NonNull
+ public WindowMetrics getParentWindowMetrics() {
+ return mParentWindowMetrics;
+ }
+
+ /** Returns the parent container's {@link Configuration} */
+ @NonNull
+ public Configuration getParentConfiguration() {
+ return new Configuration(mParentConfiguration);
+ }
+
+ /**
+ * Returns the {@link SplitRule#getDefaultSplitAttributes()}. It could be from
+ * {@link SplitRule} Builder APIs
+ * ({@link SplitPairRule.Builder#setDefaultSplitAttributes(SplitAttributes)} or
+ * {@link SplitPlaceholderRule.Builder#setDefaultSplitAttributes(SplitAttributes)}) or from
+ * the {@code splitRatio} and {@code splitLayoutDirection} attributes from static rule
+ * definitions.
+ */
+ @NonNull
+ public SplitAttributes getDefaultSplitAttributes() {
+ return mDefaultSplitAttributes;
+ }
+
+ /**
+ * Returns whether the {@link #getParentWindowMetrics()} satisfies the dimensions and aspect
+ * ratios requirements specified in the {@link androidx.window.embedding.SplitRule}, which
+ * are:
+ * - {@link androidx.window.embedding.SplitRule#minWidthDp}
+ * - {@link androidx.window.embedding.SplitRule#minHeightDp}
+ * - {@link androidx.window.embedding.SplitRule#minSmallestWidthDp}
+ * - {@link androidx.window.embedding.SplitRule#maxAspectRatioInPortrait}
+ * - {@link androidx.window.embedding.SplitRule#maxAspectRatioInLandscape}
+ */
+ public boolean isDefaultMinSizeSatisfied() {
+ return mIsDefaultMinSizeSatisfied;
+ }
+
+ /** Returns the parent container's {@link WindowLayoutInfo} */
+ @NonNull
+ public WindowLayoutInfo getParentWindowLayoutInfo() {
+ return mParentWindowLayoutInfo;
+ }
+
+ /**
+ * Returns {@link SplitRule#getTag()} to apply the {@link SplitAttributes} result if it was
+ * set.
+ */
+ @Nullable
+ public String getSplitRuleTag() {
+ return mSplitRuleTag;
+ }
+
+ SplitAttributesCalculatorParams(
+ @NonNull WindowMetrics parentWindowMetrics,
+ @NonNull Configuration parentConfiguration,
+ @NonNull SplitAttributes defaultSplitAttributes,
+ boolean isDefaultMinSizeSatisfied,
+ @NonNull WindowLayoutInfo parentWindowLayoutInfo,
+ @Nullable String splitRuleTag
+ ) {
+ mParentWindowMetrics = parentWindowMetrics;
+ mParentConfiguration = parentConfiguration;
+ mParentWindowLayoutInfo = parentWindowLayoutInfo;
+ mDefaultSplitAttributes = defaultSplitAttributes;
+ mIsDefaultMinSizeSatisfied = isDefaultMinSizeSatisfied;
+ mSplitRuleTag = splitRuleTag;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + ":{"
+ + "windowMetrics=" + windowMetricsToString(mParentWindowMetrics)
+ + ", configuration=" + mParentConfiguration
+ + ", windowLayoutInfo=" + mParentWindowLayoutInfo
+ + ", defaultSplitAttributes=" + mDefaultSplitAttributes
+ + ", isDefaultMinSizeSatisfied=" + mIsDefaultMinSizeSatisfied
+ + ", tag=" + mSplitRuleTag + "}";
+ }
+
+ private static String windowMetricsToString(@NonNull WindowMetrics windowMetrics) {
+ // TODO(b/187712731): Use WindowMetrics#toString after it's implemented in U.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ return Api30Impl.windowMetricsToString(
+ windowMetrics);
+ }
+ throw new UnsupportedOperationException("WindowMetrics didn't exist in R.");
+ }
+
+ @RequiresApi(30)
+ private static final class Api30Impl {
+ static String windowMetricsToString(@NonNull WindowMetrics windowMetrics) {
+ return WindowMetrics.class.getSimpleName() + ":{"
+ + "bounds=" + windowMetrics.getBounds()
+ + ", windowInsets=" + windowMetrics.getWindowInsets()
+ + "}";
+ }
+ }
+ }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
index 33b8bb7..bac42a4 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
@@ -16,6 +16,9 @@
package androidx.window.extensions.embedding;
+import android.os.Binder;
+import android.os.IBinder;
+
import androidx.annotation.NonNull;
import androidx.window.extensions.WindowExtensions;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
@@ -25,6 +28,9 @@
/** Describes a split of two containers with activities. */
public class SplitInfo {
+ /** Only used for compatibility with the deprecated constructor. */
+ private static final IBinder INVALID_SPLIT_INFO_TOKEN = new Binder();
+
@NonNull
private final ActivityStack mPrimaryActivityStack;
@NonNull
@@ -32,22 +38,42 @@
@NonNull
private final SplitAttributes mSplitAttributes;
+ @NonNull
+ private final IBinder mToken;
+
/**
- * The {@code SplitInfo} constructor.
+ * The {@code SplitInfo} constructor
*
- * @param primaryActivityStack The primary {@link ActivityStack}.
- * @param secondaryActivityStack The secondary {@link ActivityStack}.
- * @param splitAttributes The current {@link SplitAttributes} of this split pair.
+ * @param primaryActivityStack The primary {@link ActivityStack}
+ * @param secondaryActivityStack The secondary {@link ActivityStack}
+ * @param splitAttributes The current {@link SplitAttributes} of this split pair
+ * @param token The token to identify this split pair
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
*/
SplitInfo(@NonNull ActivityStack primaryActivityStack,
@NonNull ActivityStack secondaryActivityStack,
- @NonNull SplitAttributes splitAttributes) {
+ @NonNull SplitAttributes splitAttributes,
+ @NonNull IBinder token) {
Objects.requireNonNull(primaryActivityStack);
Objects.requireNonNull(secondaryActivityStack);
Objects.requireNonNull(splitAttributes);
+ Objects.requireNonNull(token);
mPrimaryActivityStack = primaryActivityStack;
mSecondaryActivityStack = secondaryActivityStack;
mSplitAttributes = splitAttributes;
+ mToken = token;
+ }
+
+ /**
+ * @deprecated Use the {@link WindowExtensions#VENDOR_API_LEVEL_3} version.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+ */
+ @Deprecated
+ SplitInfo(@NonNull ActivityStack primaryActivityStack,
+ @NonNull ActivityStack secondaryActivityStack,
+ @NonNull SplitAttributes splitAttributes) {
+ this(primaryActivityStack, secondaryActivityStack, splitAttributes,
+ INVALID_SPLIT_INFO_TOKEN);
}
@NonNull
@@ -84,6 +110,15 @@
return mSplitAttributes;
}
+ /**
+ * Returns a token uniquely identifying the container.
+ * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+ */
+ @NonNull
+ public IBinder getToken() {
+ return mToken;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -91,7 +126,7 @@
SplitInfo that = (SplitInfo) o;
return mSplitAttributes.equals(that.mSplitAttributes) && mPrimaryActivityStack.equals(
that.mPrimaryActivityStack) && mSecondaryActivityStack.equals(
- that.mSecondaryActivityStack);
+ that.mSecondaryActivityStack) && mToken.equals(that.mToken);
}
@Override
@@ -99,6 +134,7 @@
int result = mPrimaryActivityStack.hashCode();
result = result * 31 + mSecondaryActivityStack.hashCode();
result = result * 31 + mSplitAttributes.hashCode();
+ result = result * 31 + mToken.hashCode();
return result;
}
@@ -109,6 +145,7 @@
+ "mPrimaryActivityStack=" + mPrimaryActivityStack
+ ", mSecondaryActivityStack=" + mSecondaryActivityStack
+ ", mSplitAttributes=" + mSplitAttributes
+ + ", mToken=" + mToken
+ '}';
}
}
diff --git a/window/extensions/extensions/src/test/resources/robolectric.properties b/window/extensions/extensions/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/window/extensions/extensions/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/window/window-demos/demo/src/main/AndroidManifest.xml b/window/window-demos/demo/src/main/AndroidManifest.xml
index 5d3151e..dff69ac 100644
--- a/window/window-demos/demo/src/main/AndroidManifest.xml
+++ b/window/window-demos/demo/src/main/AndroidManifest.xml
@@ -58,7 +58,7 @@
android:exported="false"
android:configChanges="orientation|screenSize|screenLayout|screenSize"
android:label="@string/window_metrics"/>
- <activity android:name=".area.RearDisplayActivityConfigChanges"
+ <activity android:name=".RearDisplayActivityConfigChanges"
android:exported="true"
android:configChanges=
"orientation|screenLayout|screenSize|layoutDirection|smallestScreenSize"
@@ -248,6 +248,20 @@
android:taskAffinity="androidx.window.demo.split_ime">
</activity>
+ <!-- The demo app to show display configuration from different system callbacks. -->
+
+ <activity
+ android:name=".coresdk.WindowStateCallbackActivity"
+ android:exported="true"
+ android:label="Window State Callbacks"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+ android:taskAffinity="androidx.window.demo.window_state_callback">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
<!-- Activity embedding initializer -->
<provider android:name="androidx.startup.InitializationProvider"
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt
new file mode 100644
index 0000000..3d35924
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.area.WindowAreaCapability
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
+import androidx.window.area.WindowAreaController
+import androidx.window.area.WindowAreaInfo
+import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
+import androidx.window.area.WindowAreaSession
+import androidx.window.area.WindowAreaSessionCallback
+import androidx.window.demo.common.infolog.InfoLogAdapter
+import androidx.window.demo.databinding.ActivityRearDisplayBinding
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import java.util.concurrent.Executor
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+/**
+ * Demo Activity that showcases listening for RearDisplay Status
+ * as well as enabling/disabling RearDisplay mode. This Activity
+ * implements [WindowAreaSessionCallback] for simplicity.
+ *
+ * This Activity overrides configuration changes for simplicity.
+ */
+class RearDisplayActivityConfigChanges : AppCompatActivity(), WindowAreaSessionCallback {
+
+ private lateinit var windowAreaController: WindowAreaController
+ private var rearDisplaySession: WindowAreaSession? = null
+ private var rearDisplayWindowAreaInfo: WindowAreaInfo? = null
+ private var rearDisplayStatus: WindowAreaCapability.Status = WINDOW_AREA_STATUS_UNSUPPORTED
+ private val infoLogAdapter = InfoLogAdapter()
+ private lateinit var binding: ActivityRearDisplayBinding
+ private lateinit var executor: Executor
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityRearDisplayBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ executor = ContextCompat.getMainExecutor(this)
+ windowAreaController = WindowAreaController.getOrCreate()
+
+ binding.rearStatusRecyclerView.adapter = infoLogAdapter
+
+ binding.rearDisplayButton.setOnClickListener {
+ if (rearDisplayStatus == WINDOW_AREA_STATUS_ACTIVE) {
+ if (rearDisplaySession == null) {
+ rearDisplaySession = rearDisplayWindowAreaInfo?.getActiveSession(
+ OPERATION_TRANSFER_ACTIVITY_TO_AREA
+ )
+ }
+ rearDisplaySession?.close()
+ } else {
+ rearDisplayWindowAreaInfo?.token?.let { token ->
+ windowAreaController.transferActivityToWindowArea(
+ token = token,
+ activity = this,
+ executor = executor,
+ windowAreaSessionCallback = this)
+ }
+ }
+ }
+
+ lifecycleScope.launch(Dispatchers.Main) {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ windowAreaController
+ .windowAreaInfos
+ .map { windowAreaInfoList -> windowAreaInfoList.firstOrNull {
+ windowAreaInfo -> windowAreaInfo.type == TYPE_REAR_FACING
+ } }
+ .onEach { windowAreaInfo -> rearDisplayWindowAreaInfo = windowAreaInfo }
+ .map(this@RearDisplayActivityConfigChanges::getRearDisplayStatus)
+ .distinctUntilChanged()
+ .collect { status ->
+ infoLogAdapter.append(getCurrentTimeString(), status.toString())
+ infoLogAdapter.notifyDataSetChanged()
+ rearDisplayStatus = status
+ updateRearDisplayButton()
+ }
+ }
+ }
+ }
+
+ override fun onSessionStarted(session: WindowAreaSession) {
+ rearDisplaySession = session
+ infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has been started")
+ infoLogAdapter.notifyDataSetChanged()
+ updateRearDisplayButton()
+ }
+
+ override fun onSessionEnded(t: Throwable?) {
+ rearDisplaySession = null
+ infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has ended")
+ infoLogAdapter.notifyDataSetChanged()
+ updateRearDisplayButton()
+ }
+
+ private fun updateRearDisplayButton() {
+ if (rearDisplaySession != null) {
+ binding.rearDisplayButton.isEnabled = true
+ binding.rearDisplayButton.text = "Disable RearDisplay Mode"
+ return
+ }
+ when (rearDisplayStatus) {
+ WINDOW_AREA_STATUS_UNSUPPORTED -> {
+ binding.rearDisplayButton.isEnabled = false
+ binding.rearDisplayButton.text = "RearDisplay is not supported on this device"
+ }
+ WINDOW_AREA_STATUS_UNAVAILABLE -> {
+ binding.rearDisplayButton.isEnabled = false
+ binding.rearDisplayButton.text = "RearDisplay is not currently available"
+ }
+ WINDOW_AREA_STATUS_AVAILABLE -> {
+ binding.rearDisplayButton.isEnabled = true
+ binding.rearDisplayButton.text = "Enable RearDisplay Mode"
+ }
+ WINDOW_AREA_STATUS_ACTIVE -> {
+ binding.rearDisplayButton.isEnabled = true
+ binding.rearDisplayButton.text = "Disable RearDisplay Mode"
+ }
+ }
+ }
+
+ private fun getCurrentTimeString(): String {
+ val sdf = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
+ val currentDate = sdf.format(Date())
+ return currentDate.toString()
+ }
+
+ private fun getRearDisplayStatus(windowAreaInfo: WindowAreaInfo?): WindowAreaCapability.Status {
+ val status = windowAreaInfo?.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA)?.status
+ return status ?: WINDOW_AREA_STATUS_UNSUPPORTED
+ }
+
+ private companion object {
+ private val TAG = RearDisplayActivityConfigChanges::class.java.simpleName
+ }
+}
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
deleted file mode 100644
index edb2ed1..0000000
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.demo.area
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
-import androidx.core.util.Consumer
-import androidx.window.area.WindowAreaController
-import androidx.window.area.WindowAreaSession
-import androidx.window.area.WindowAreaSessionCallback
-import androidx.window.area.WindowAreaStatus
-import androidx.window.core.ExperimentalWindowApi
-import androidx.window.demo.common.infolog.InfoLogAdapter
-import androidx.window.demo.databinding.ActivityRearDisplayBinding
-import androidx.window.java.area.WindowAreaControllerJavaAdapter
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import java.util.concurrent.Executor
-
-/**
- * Demo Activity that showcases listening for RearDisplay Status
- * as well as enabling/disabling RearDisplay mode. This Activity
- * implements [WindowAreaSessionCallback] for simplicity.
- *
- * This Activity overrides configuration changes for simplicity.
- */
-@OptIn(ExperimentalWindowApi::class)
-class RearDisplayActivityConfigChanges : AppCompatActivity(), WindowAreaSessionCallback {
-
- private lateinit var windowAreaController: WindowAreaControllerJavaAdapter
- private var rearDisplaySession: WindowAreaSession? = null
- private val infoLogAdapter = InfoLogAdapter()
- private lateinit var binding: ActivityRearDisplayBinding
- private lateinit var executor: Executor
-
- private val rearDisplayStatusListener = Consumer<WindowAreaStatus> { status ->
- infoLogAdapter.append(getCurrentTimeString(), status.toString())
- infoLogAdapter.notifyDataSetChanged()
- updateRearDisplayButton(status)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityRearDisplayBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
- executor = ContextCompat.getMainExecutor(this)
- windowAreaController = WindowAreaControllerJavaAdapter(WindowAreaController.getOrCreate())
-
- binding.rearStatusRecyclerView.adapter = infoLogAdapter
-
- binding.rearDisplayButton.setOnClickListener {
- if (rearDisplaySession != null) {
- rearDisplaySession?.close()
- } else {
- windowAreaController.startRearDisplayModeSession(
- this,
- executor,
- this)
- }
- }
- }
-
- override fun onStart() {
- super.onStart()
- windowAreaController.addRearDisplayStatusListener(
- executor,
- rearDisplayStatusListener
- )
- }
-
- override fun onStop() {
- super.onStop()
- windowAreaController.removeRearDisplayStatusListener(rearDisplayStatusListener)
- }
-
- override fun onSessionStarted(session: WindowAreaSession) {
- rearDisplaySession = session
- infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has been started")
- infoLogAdapter.notifyDataSetChanged()
- }
-
- override fun onSessionEnded() {
- rearDisplaySession = null
- infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has ended")
- infoLogAdapter.notifyDataSetChanged()
- }
-
- private fun updateRearDisplayButton(status: WindowAreaStatus) {
- if (rearDisplaySession != null) {
- binding.rearDisplayButton.isEnabled = true
- binding.rearDisplayButton.text = "Disable RearDisplay Mode"
- return
- }
- when (status) {
- WindowAreaStatus.UNSUPPORTED -> {
- binding.rearDisplayButton.isEnabled = false
- binding.rearDisplayButton.text = "RearDisplay is not supported on this device"
- }
- WindowAreaStatus.UNAVAILABLE -> {
- binding.rearDisplayButton.isEnabled = false
- binding.rearDisplayButton.text = "RearDisplay is not currently available"
- }
- WindowAreaStatus.AVAILABLE -> {
- binding.rearDisplayButton.isEnabled = true
- binding.rearDisplayButton.text = "Enable RearDisplay Mode"
- }
- }
- }
-
- private fun getCurrentTimeString(): String {
- val sdf = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
- val currentDate = sdf.format(Date())
- return currentDate.toString()
- }
-
- private companion object {
- private val TAG = RearDisplayActivityConfigChanges::class.java.simpleName
- }
-}
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt
new file mode 100644
index 0000000..bd39f74
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.coresdk
+
+import android.content.Context
+import android.content.res.Configuration
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.demo.databinding.ActivityCoresdkWindowStateCallbackLayoutBinding
+import androidx.window.layout.WindowInfoTracker
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+/** Activity to show display configuration from different system callbacks. */
+class WindowStateCallbackActivity : AppCompatActivity() {
+
+ private lateinit var displayManager: DisplayManager
+ private lateinit var handler: Handler
+
+ private lateinit var latestUpdateView: WindowStateView
+ private lateinit var displayListenerView: WindowStateView
+ private lateinit var activityConfigurationView: WindowStateView
+ private lateinit var displayFeatureView: WindowStateView
+
+ /**
+ * Runnable to poll configuration every 500ms.
+ * To always provide an up-to-date configuration so it can be used to verify the configurations
+ * from other callbacks.
+ */
+ private val updateWindowStateIfChanged = object : Runnable {
+ override fun run() {
+ latestUpdateView.onWindowStateCallbackInvoked()
+ handler.postDelayed(this, 500)
+ }
+ }
+
+ private val displayListener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {
+ }
+
+ override fun onDisplayChanged(displayId: Int) {
+ if (displayId == DEFAULT_DISPLAY) {
+ displayListenerView.onWindowStateCallbackInvoked()
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val viewBinding = ActivityCoresdkWindowStateCallbackLayoutBinding.inflate(layoutInflater)
+ setContentView(viewBinding.root)
+ latestUpdateView = viewBinding.latestUpdateView
+ displayListenerView = viewBinding.displayListenerView
+ activityConfigurationView = viewBinding.activityConfigurationView
+ displayFeatureView = viewBinding.displayFeatureView
+
+ displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(displayListener, handler)
+ // Collect windowInfo when STARTED and stop when STOPPED.
+ lifecycleScope.launch(Dispatchers.Main) {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ WindowInfoTracker.getOrCreate(this@WindowStateCallbackActivity)
+ .windowLayoutInfo(this@WindowStateCallbackActivity)
+ .collect { _ ->
+ displayFeatureView.onWindowStateCallbackInvoked()
+ }
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ displayManager.unregisterDisplayListener(displayListener)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ // Start polling the configuration every 500ms.
+ updateWindowStateIfChanged.run()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ handler.removeCallbacks(updateWindowStateIfChanged)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ activityConfigurationView.onWindowStateCallbackInvoked()
+ }
+}
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt
new file mode 100644
index 0000000..4a18150
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.coresdk
+
+import android.content.Context
+import android.graphics.Color
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.window.demo.R
+import androidx.window.demo.databinding.WindowStateConfigViewBinding
+
+/** View to show a display configuration value. */
+class WindowStateConfigView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val configView: TextView
+ private var configValue: String? = null
+
+ /** Whether to highlight the value when it is changed. */
+ var shouldHighlightChange = false
+
+ init {
+ val viewBinding = WindowStateConfigViewBinding.inflate(LayoutInflater.from(context),
+ this, true)
+ configView = viewBinding.configValue
+ context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.WindowStateConfigView,
+ 0, 0).apply {
+ try {
+ getString(R.styleable.WindowStateConfigView_configName)?.let {
+ viewBinding.configName.text = it
+ }
+ } finally {
+ recycle()
+ }
+ }
+ }
+
+ /** Updates the config value. */
+ fun updateValue(value: String) {
+ if (shouldHighlightChange && configValue != null && configValue != value) {
+ // Highlight previous value if changed.
+ configView.setTextColor(Color.RED)
+ } else {
+ configView.setTextColor(Color.BLACK)
+ }
+ configValue = value
+ configView.text = value
+ }
+}
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt
new file mode 100644
index 0000000..c569e8f
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.coresdk
+
+import android.content.Context
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.util.AttributeSet
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.LayoutInflater
+import android.view.WindowManager
+import android.widget.LinearLayout
+import androidx.window.demo.R
+import androidx.window.demo.databinding.WindowStateViewBinding
+import java.text.SimpleDateFormat
+import java.util.Date
+
+/** View to show the display configuration from the latest update. */
+class WindowStateView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val displayManager: DisplayManager
+ private val windowManager: WindowManager
+
+ private val timestampView: WindowStateConfigView
+ private val displayRotationView: WindowStateConfigView
+ private val displayBoundsView: WindowStateConfigView
+ private val prevDisplayRotationView: WindowStateConfigView
+ private val prevDisplayBoundsView: WindowStateConfigView
+
+ private val shouldHidePrevConfig: Boolean
+ private var lastDisplayRotation = -1
+ private val lastDisplayBounds = Rect()
+
+ init {
+ displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+
+ val viewBinding = WindowStateViewBinding.inflate(LayoutInflater.from(context), this, true)
+ timestampView = viewBinding.timestampView
+ displayRotationView = viewBinding.displayRotationView
+ displayBoundsView = viewBinding.displayBoundsView
+ prevDisplayRotationView = viewBinding.prevDisplayRotationView
+ prevDisplayBoundsView = viewBinding.prevDisplayBoundsView
+
+ context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.WindowStateView,
+ 0, 0).apply {
+ try {
+ getString(R.styleable.WindowStateView_title)?.let {
+ viewBinding.callbackTitle.text = it
+ }
+ shouldHidePrevConfig = getBoolean(
+ R.styleable.WindowStateView_hidePrevConfig,
+ false)
+ if (shouldHidePrevConfig) {
+ timestampView.visibility = GONE
+ displayRotationView.shouldHighlightChange = false
+ displayBoundsView.shouldHighlightChange = false
+ prevDisplayRotationView.visibility = GONE
+ prevDisplayBoundsView.visibility = GONE
+ } else {
+ displayRotationView.shouldHighlightChange = true
+ displayBoundsView.shouldHighlightChange = true
+ }
+ } finally {
+ recycle()
+ }
+ }
+ }
+
+ /** Called when the corresponding system callback is invoked. */
+ fun onWindowStateCallbackInvoked() {
+ val displayRotation = displayManager.getDisplay(DEFAULT_DISPLAY).rotation
+ val displayBounds = windowManager.maximumWindowMetrics.bounds
+
+ if (shouldHidePrevConfig &&
+ displayRotation == lastDisplayRotation &&
+ displayBounds == lastDisplayBounds) {
+ // Skip if the state is unchanged.
+ return
+ }
+
+ timestampView.updateValue(TIME_FORMAT.format(Date()))
+ displayRotationView.updateValue(displayRotation.toString())
+ displayBoundsView.updateValue(displayBounds.toString())
+
+ if (!shouldHidePrevConfig && lastDisplayRotation != -1) {
+ // Skip if there is no previous value.
+ prevDisplayRotationView.updateValue(lastDisplayRotation.toString())
+ prevDisplayBoundsView.updateValue(lastDisplayBounds.toString())
+ }
+
+ lastDisplayRotation = displayRotation
+ lastDisplayBounds.set(displayBounds)
+ }
+
+ companion object {
+ private val TIME_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
+ }
+}
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
index a5fe754..505a365 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
@@ -18,7 +18,6 @@
import android.content.Context
import androidx.startup.Initializer
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.demo.R
import androidx.window.demo.embedding.SplitDeviceStateActivityBase.Companion.SUFFIX_AND_FULLSCREEN_IN_BOOK_MODE
import androidx.window.demo.embedding.SplitDeviceStateActivityBase.Companion.SUFFIX_AND_HORIZONTAL_LAYOUT_IN_TABLETOP
@@ -46,7 +45,6 @@
/**
* Initializes SplitController with a set of statically defined rules.
*/
-@OptIn(ExperimentalWindowApi::class)
class ExampleWindowInitializer : Initializer<RuleController> {
override fun create(context: Context): RuleController {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
index 8e0c564..faf792e 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
@@ -24,6 +24,7 @@
import static androidx.window.embedding.SplitRule.FinishBehavior.NEVER;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -41,6 +42,7 @@
import androidx.window.demo.R;
import androidx.window.demo.databinding.ActivitySplitActivityLayoutBinding;
import androidx.window.embedding.ActivityEmbeddingController;
+import androidx.window.embedding.ActivityEmbeddingOptions;
import androidx.window.embedding.ActivityFilter;
import androidx.window.embedding.ActivityRule;
import androidx.window.embedding.EmbeddingRule;
@@ -96,8 +98,23 @@
bStartIntent.putExtra(EXTRA_LAUNCH_C_TO_SIDE, true);
startActivity(bStartIntent);
});
- mViewBinding.launchE.setOnClickListener((View v) ->
- startActivity(new Intent(this, SplitActivityE.class)));
+ mViewBinding.launchE.setOnClickListener((View v) -> {
+ Bundle bundle = null;
+ if (mViewBinding.setLaunchingEInActivityStack.isChecked()) {
+ try {
+ final ActivityOptions options = ActivityEmbeddingOptions
+ .setLaunchingActivityStack(ActivityOptions.makeBasic(), this);
+ bundle = options.toBundle();
+ } catch (UnsupportedOperationException ex) {
+ Log.w(TAG, "#setLaunchingActivityStack is not supported", ex);
+ }
+ }
+ startActivity(new Intent(this, SplitActivityE.class), bundle);
+ });
+ if (!ActivityEmbeddingOptions.isSetLaunchingActivityStackSupported(
+ ActivityOptions.makeBasic())) {
+ mViewBinding.setLaunchingEInActivityStack.setEnabled(false);
+ }
mViewBinding.launchF.setOnClickListener((View v) ->
startActivity(new Intent(this, SplitActivityF.class)));
mViewBinding.launchFPendingIntent.setOnClickListener((View v) -> {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
index 9f80a5f..e95997c 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
@@ -28,7 +28,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.demo.R
import androidx.window.demo.databinding.ActivitySplitDeviceStateLayoutBinding
import androidx.window.embedding.EmbeddingRule
@@ -45,7 +44,6 @@
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-@OptIn(ExperimentalWindowApi::class)
open class SplitDeviceStateActivityBase : AppCompatActivity(), View.OnClickListener,
RadioGroup.OnCheckedChangeListener, CompoundButton.OnCheckedChangeListener,
AdapterView.OnItemSelectedListener {
diff --git a/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml
new file mode 100644
index 0000000..64c3b56
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/root_split_activity_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dp">
+
+ <!-- Update to the latest configuration. -->
+ <androidx.window.demo.coresdk.WindowStateView
+ android:id="@+id/latest_update_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:title="@string/latest_configuration_title"
+ app:hidePrevConfig="true"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:background="#AAAAAA" />
+
+ <!-- Update from DisplayListener. -->
+ <androidx.window.demo.coresdk.WindowStateView
+ android:id="@+id/display_listener_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:title="@string/display_listener_title"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:background="#AAAAAA" />
+
+ <!-- Update from Activity#onConfigurationChanged. -->
+ <androidx.window.demo.coresdk.WindowStateView
+ android:id="@+id/activity_configuration_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:title="@string/activity_configuration_title"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:background="#AAAAAA" />
+
+ <!-- Update from WindowInfoTracker. -->
+ <androidx.window.demo.coresdk.WindowStateView
+ android:id="@+id/display_feature_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:title="@string/display_feature_title"/>
+
+ </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
index 099490a..dcc8ab4 100644
--- a/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
+++ b/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
@@ -161,4 +161,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
-</ScrollView>
+</ScrollView>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml b/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml
new file mode 100644
index 0000000..6a97d8f
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/config_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="@string/window_state_placeholder"/>
+
+ <TextView
+ android:id="@+id/config_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/window_state_placeholder"
+ android:layout_marginLeft="10dp"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/window_state_view.xml b/window/window-demos/demo/src/main/res/layout/window_state_view.xml
new file mode 100644
index 0000000..1944b95
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/window_state_view.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Callback name -->
+ <TextView
+ android:id="@+id/callback_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.Material.Title"
+ android:text="@string/window_state_placeholder"/>
+
+ <!-- Last update timestamp -->
+ <androidx.window.demo.coresdk.WindowStateConfigView
+ android:id="@+id/timestamp_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:configName="@string/timestamp_title"/>
+
+ <!-- Display rotation -->
+ <androidx.window.demo.coresdk.WindowStateConfigView
+ android:id="@+id/display_rotation_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:configName="@string/display_rotation_title"/>
+
+ <!-- Display bounds -->
+ <androidx.window.demo.coresdk.WindowStateConfigView
+ android:id="@+id/display_bounds_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:configName="@string/display_bounds_title"/>
+
+ <!-- Previous Display rotation -->
+ <androidx.window.demo.coresdk.WindowStateConfigView
+ android:id="@+id/prev_display_rotation_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:configName="@string/prev_display_rotation_title"/>
+
+ <!-- Previous Display bounds -->
+ <androidx.window.demo.coresdk.WindowStateConfigView
+ android:id="@+id/prev_display_bounds_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:configName="@string/prev_display_bounds_title"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/values/attrs.xml b/window/window-demos/demo/src/main/res/values/attrs.xml
index 8a29d91..34b783a 100644
--- a/window/window-demos/demo/src/main/res/values/attrs.xml
+++ b/window/window-demos/demo/src/main/res/values/attrs.xml
@@ -20,4 +20,13 @@
<attr name="startViewId" format="reference" />
<attr name="endViewId" format="reference" />
</declare-styleable>
+
+ <declare-styleable name="WindowStateView">
+ <attr name="title" format="string" />
+ <attr name="hidePrevConfig" format="boolean" />
+ </declare-styleable>
+
+ <declare-styleable name="WindowStateConfigView">
+ <attr name="configName" format="string" />
+ </declare-styleable>
</resources>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/values/strings.xml b/window/window-demos/demo/src/main/res/values/strings.xml
index 1a86d90..e9ce55f 100644
--- a/window/window-demos/demo/src/main/res/values/strings.xml
+++ b/window/window-demos/demo/src/main/res/values/strings.xml
@@ -58,4 +58,14 @@
<string name="ime_button_switch_default">Switch default IME</string>
<string name="install_samples_2">Install window-demos:demo-second-app to launch activities from a different UID.</string>
<string name="toast_split_not_support">Please enable PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED and ensure device supports splits.</string>
+ <string name="latest_configuration_title">Latest Configuration</string>
+ <string name="display_listener_title">DisplayListener#onDisplayChanged</string>
+ <string name="activity_configuration_title">Activity#onConfigurationChanged</string>
+ <string name="display_feature_title">WindowInfoTracker</string>
+ <string name="timestamp_title">Timestamp:</string>
+ <string name="display_rotation_title">Display Rotation:</string>
+ <string name="display_bounds_title">Display Bounds:</string>
+ <string name="prev_display_rotation_title">Previous Display Rotation:</string>
+ <string name="prev_display_bounds_title">Previous Display Bounds:</string>
+ <string name="window_state_placeholder">N/A</string>
</resources>
diff --git a/window/window-java/api/current.txt b/window/window-java/api/current.txt
index d621966..d443b31 100644
--- a/window/window-java/api/current.txt
+++ b/window/window-java/api/current.txt
@@ -1,4 +1,14 @@
// Signature format: 4.0
+package androidx.window.java.area {
+
+ public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+ ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
+ method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+ method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+ }
+
+}
+
package androidx.window.java.embedding {
@androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
diff --git a/window/window-java/api/restricted_current.txt b/window/window-java/api/restricted_current.txt
index d621966..d443b31 100644
--- a/window/window-java/api/restricted_current.txt
+++ b/window/window-java/api/restricted_current.txt
@@ -1,4 +1,14 @@
// Signature format: 4.0
+package androidx.window.java.area {
+
+ public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+ ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
+ method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+ method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+ }
+
+}
+
package androidx.window.java.embedding {
@androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
diff --git a/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt b/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt
new file mode 100644
index 0000000..09c8292
--- /dev/null
+++ b/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.java.area
+
+import androidx.core.util.Consumer
+import androidx.window.area.WindowAreaController
+import androidx.window.area.WindowAreaInfo
+import java.util.concurrent.Executor
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.launch
+
+/**
+ * An adapter for [WindowAreaController] to provide callback APIs.
+ */
+class WindowAreaControllerCallbackAdapter(
+ private val controller: WindowAreaController
+) : WindowAreaController by controller {
+
+ /**
+ * A [ReentrantLock] to protect against concurrent access to [consumerToJobMap].
+ */
+ private val lock = ReentrantLock()
+ private val consumerToJobMap = mutableMapOf<Consumer<*>, Job>()
+
+ /**
+ * Registers a listener that is interested in the current list of [WindowAreaInfo] available to
+ * be interacted with.
+ *
+ * The [listener] will receive an initial value on registration, as soon as it becomes
+ * available.
+ *
+ * @param executor to handle sending listener updates.
+ * @param listener to receive updates to the list of [WindowAreaInfo].
+ * @see WindowAreaController.transferActivityToWindowArea
+ * @see WindowAreaController.presentContentOnWindowArea
+ */
+ fun addWindowAreaInfoListListener(
+ executor: Executor,
+ listener: Consumer<List<WindowAreaInfo>>
+ ) {
+ // TODO(274013517): Extract adapter pattern out of each class
+ val statusFlow = controller.windowAreaInfos
+ lock.withLock {
+ if (consumerToJobMap[listener] == null) {
+ val scope = CoroutineScope(executor.asCoroutineDispatcher())
+ consumerToJobMap[listener] = scope.launch {
+ statusFlow.collect { listener.accept(it) }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a listener of available [WindowAreaInfo] records. If the listener is not present then
+ * this method is a no-op.
+ *
+ * @param listener to remove from receiving status updates.
+ * @see WindowAreaController.transferActivityToWindowArea
+ * @see WindowAreaController.presentContentOnWindowArea
+ */
+ fun removeWindowAreaInfoListListener(listener: Consumer<List<WindowAreaInfo>>) {
+ lock.withLock {
+ consumerToJobMap[listener]?.cancel()
+ consumerToJobMap.remove(listener)
+ }
+ }
+ }
\ No newline at end of file
diff --git a/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerJavaAdapter.kt b/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerJavaAdapter.kt
deleted file mode 100644
index 3ea0c05..0000000
--- a/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerJavaAdapter.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.java.area
-
-import android.app.Activity
-import androidx.core.util.Consumer
-import androidx.window.area.WindowAreaController
-import androidx.window.area.WindowAreaSessionCallback
-import androidx.window.area.WindowAreaStatus
-import androidx.window.core.ExperimentalWindowApi
-import java.util.concurrent.Executor
-import java.util.concurrent.locks.ReentrantLock
-import kotlin.concurrent.withLock
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.asCoroutineDispatcher
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-
-/**
- * An adapted interface for [WindowAreaController] that provides the information and
- * functionality around RearDisplay Mode via a callback shaped API.
- *
- * @hide
- *
- */
-@ExperimentalWindowApi
-class WindowAreaControllerJavaAdapter(
- private val controller: WindowAreaController
-) : WindowAreaController by controller {
-
- /**
- * A [ReentrantLock] to protect against concurrent access to [consumerToJobMap].
- */
- private val lock = ReentrantLock()
- private val consumerToJobMap = mutableMapOf<Consumer<*>, Job>()
-
- /**
- * Registers a listener to consume [WindowAreaStatus] values defined as
- * [WindowAreaStatus.UNSUPPORTED], [WindowAreaStatus.UNAVAILABLE], and
- * [WindowAreaStatus.AVAILABLE]. The values provided through this listener should be used
- * to determine if you are able to enable rear display Mode at that time. You can use these
- * values to modify your UI to show/hide controls and determine when to enable features
- * that use rear display Mode. You should only try and enter rear display mode when your
- * [consumer] is provided a value of [WindowAreaStatus.AVAILABLE].
- *
- * The [consumer] will be provided an initial value on registration, as well as any change
- * to the status as they occur. This could happen due to hardware device state changes, or if
- * another process has enabled RearDisplay Mode.
- *
- * @see WindowAreaController.rearDisplayStatus
- */
- fun addRearDisplayStatusListener(
- executor: Executor,
- consumer: Consumer<WindowAreaStatus>
- ) {
- val statusFlow = controller.rearDisplayStatus()
- lock.withLock {
- if (consumerToJobMap[consumer] == null) {
- val scope = CoroutineScope(executor.asCoroutineDispatcher())
- consumerToJobMap[consumer] = scope.launch {
- statusFlow.collect { consumer.accept(it) }
- }
- }
- }
- }
-
- /**
- * Removes a listener of [WindowAreaStatus] values
- * @see WindowAreaController.rearDisplayStatus
- */
- fun removeRearDisplayStatusListener(consumer: Consumer<WindowAreaStatus>) {
- lock.withLock {
- consumerToJobMap[consumer]?.cancel()
- consumerToJobMap.remove(consumer)
- }
- }
-
- /**
- * Starts a RearDisplay Mode session and provides updates through the
- * [WindowAreaSessionCallback] provided. Due to the nature of moving your Activity to a
- * different display, your Activity will likely go through a configuration change. Because of
- * this, if your Activity does not override configuration changes, this method should be called
- * from a component that outlives the Activity lifecycle such as a
- * [androidx.lifecycle.ViewModel]. If your Activity does override
- * configuration changes, it is safe to call this method inside your Activity.
- *
- * This method should only be called if you have received a [WindowAreaStatus.AVAILABLE]
- * value from the listener provided through the [addRearDisplayStatusListener] method. If
- * you try and enable RearDisplay mode without it being available, you will receive an
- * [UnsupportedOperationException].
- *
- * The [windowAreaSessionCallback] provided will receive a call to
- * [WindowAreaSessionCallback.onSessionStarted] after your Activity has been moved to the
- * display corresponding to this mode. RearDisplay mode will stay active until the session
- * provided through [WindowAreaSessionCallback.onSessionStarted] is closed, or there is a device
- * state change that makes RearDisplay mode incompatible such as if the device is closed so the
- * outer-display is no longer in line with the rear camera. When this occurs,
- * [WindowAreaSessionCallback.onSessionEnded] is called to notify you the session has been
- * ended.
- *
- * @see addRearDisplayStatusListener
- * @throws UnsupportedOperationException if you try and start a RearDisplay session when
- * your [WindowAreaController.rearDisplayStatus] does not return a value of
- * [WindowAreaStatus.AVAILABLE]
- */
- fun startRearDisplayModeSession(
- activity: Activity,
- executor: Executor,
- windowAreaSessionCallback: WindowAreaSessionCallback
- ) {
- controller.rearDisplayMode(activity, executor, windowAreaSessionCallback)
- }
-}
\ No newline at end of file
diff --git a/window/window-testing/lint-baseline.xml b/window/window-testing/lint-baseline.xml
new file mode 100644
index 0000000..4225368
--- /dev/null
+++ b/window/window-testing/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.1.0-alpha07">
+
+ <issue
+ id="BanHideAnnotation"
+ message="@hide is not allowed in Javadoc"
+ errorLine1="val TEST_ACTIVITY_STACK_TOKEN = Binder()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt"/>
+ </issue>
+
+</issues>
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
index ef38d1a..396936b 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
@@ -18,6 +18,9 @@
package androidx.window.testing.embedding
import android.app.Activity
+import android.os.Binder
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import androidx.window.embedding.ActivityStack
/**
@@ -37,4 +40,10 @@
fun TestActivityStack(
activitiesInProcess: List<Activity> = emptyList(),
isEmpty: Boolean = false,
-): ActivityStack = ActivityStack(activitiesInProcess, isEmpty)
\ No newline at end of file
+): ActivityStack = ActivityStack(activitiesInProcess, isEmpty, TEST_ACTIVITY_STACK_TOKEN)
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@VisibleForTesting
+@JvmField
+val TEST_ACTIVITY_STACK_TOKEN = Binder()
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
index dd17311..4b39749 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTesting.kt
@@ -18,7 +18,6 @@
package androidx.window.testing.embedding
import android.content.res.Configuration
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.SplitAttributes
import androidx.window.embedding.SplitAttributesCalculatorParams
import androidx.window.embedding.SplitController
@@ -55,7 +54,6 @@
*
* @see SplitAttributesCalculatorParams
*/
-@OptIn(ExperimentalWindowApi::class)
@Suppress("FunctionName")
@JvmName("createTestSplitAttributesCalculatorParams")
@JvmOverloads
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
index e3d3ac9..1ed7844 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
@@ -17,6 +17,7 @@
package androidx.window.testing.embedding
+import android.os.Binder
import androidx.window.embedding.ActivityStack
import androidx.window.embedding.SplitAttributes
import androidx.window.embedding.SplitInfo
@@ -42,4 +43,11 @@
primaryActivityStack: ActivityStack = TestActivityStack(),
secondActivityStack: ActivityStack = TestActivityStack(),
splitAttributes: SplitAttributes = SplitAttributes.Builder().build(),
-): SplitInfo = SplitInfo(primaryActivityStack, secondActivityStack, splitAttributes)
\ No newline at end of file
+): SplitInfo = SplitInfo(
+ primaryActivityStack,
+ secondActivityStack,
+ splitAttributes,
+ TEST_SPLIT_INFO_TOKEN
+)
+
+private val TEST_SPLIT_INFO_TOKEN = Binder()
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
index 3b77116..78aca27 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
@@ -17,8 +17,10 @@
package androidx.window.testing.embedding
import android.app.Activity
+import android.app.ActivityOptions
+import android.os.IBinder
import androidx.core.util.Consumer
-import androidx.window.core.ExperimentalWindowApi
+import androidx.window.embedding.ActivityStack
import androidx.window.embedding.EmbeddingBackend
import androidx.window.embedding.EmbeddingRule
import androidx.window.embedding.SplitAttributes
@@ -147,7 +149,6 @@
override fun isActivityEmbedded(activity: Activity): Boolean =
embeddedActivities.contains(activity)
- @ExperimentalWindowApi
override fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
) {
@@ -162,6 +163,37 @@
TODO("Not yet implemented")
}
+ override fun getActivityStack(activity: Activity): ActivityStack? {
+ TODO("Not yet implemented")
+ }
+
+ override fun setLaunchingActivityStack(
+ options: ActivityOptions,
+ token: IBinder
+ ): ActivityOptions {
+ TODO("Not yet implemented")
+ }
+
+ override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+ TODO("Not yet implemented")
+ }
+
+ override fun isFinishActivityStacksSupported(): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun invalidateTopVisibleSplitAttributes() {
+ TODO("Not yet implemented")
+ }
+
+ override fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes) {
+ TODO("Not yet implemented")
+ }
+
+ override fun areSplitAttributesUpdatesSupported(): Boolean {
+ TODO("Not yet implemented")
+ }
+
private fun validateRules(rules: Set<EmbeddingRule>) {
val tags = HashSet<String>()
rules.forEach { rule ->
@@ -174,4 +206,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
index ca3d9c4..e946b53 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
@@ -40,8 +40,8 @@
public void testActivityStackDefaultValue() {
final ActivityStack activityStack = TestActivityStack.createTestActivityStack();
- assertEquals(new ActivityStack(Collections.emptyList(), false /* isEmpty */),
- activityStack);
+ assertEquals(new ActivityStack(Collections.emptyList(), false /* isEmpty */,
+ TestActivityStack.TEST_ACTIVITY_STACK_TOKEN), activityStack);
}
/** Verifies {@link TestActivityStack} */
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingTest.kt
index 905bb8f..487aeab 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingTest.kt
@@ -34,7 +34,10 @@
fun testActivityStackDefaultValue() {
val activityStack = TestActivityStack()
- assertEquals(ActivityStack(emptyList(), isEmpty = false), activityStack)
+ assertEquals(
+ ActivityStack(emptyList(), isEmpty = false, TEST_ACTIVITY_STACK_TOKEN),
+ activityStack
+ )
}
/** Verifies [TestActivityStack] */
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
index c2bb377..27e70c2 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
@@ -48,7 +48,6 @@
import java.util.List;
/** Test class to verify {@link TestSplitAttributesCalculatorParams} in Java. */
-@OptIn(markerClass = ExperimentalWindowApi.class)
@RunWith(RobolectricTestRunner.class)
public class SplitAttributesCalculatorParamsTestingJavaTest {
private static final Rect TEST_BOUNDS = new Rect(0, 0, 2000, 2000);
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
index 66dc2e8..b4d1e35 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
@@ -37,7 +37,6 @@
import org.robolectric.RobolectricTestRunner
/** Test class to verify [TestSplitAttributesCalculatorParams]. */
-@OptIn(ExperimentalWindowApi::class)
@RunWith(RobolectricTestRunner::class)
class SplitAttributesCalculatorParamsTestingTest {
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/StubEmbeddingBackendTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/StubEmbeddingBackendTest.kt
index 537e4c1..4afd0ce 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/StubEmbeddingBackendTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/StubEmbeddingBackendTest.kt
@@ -33,7 +33,6 @@
@Test
fun removingSplitInfoListenerClearsListeners() {
- val backend = StubEmbeddingBackend()
val mockActivity = mock<Activity>()
val mockCallback = mock<Consumer<List<SplitInfo>>>()
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt
new file mode 100644
index 0000000..8c261ef
--- /dev/null
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.testing.embedding
+
+import android.app.Activity
+import android.os.Binder
+import android.os.IBinder
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.embedding.ActivityStack
+import androidx.window.embedding.SplitAttributes
+import androidx.window.embedding.SplitInfo
+
+/**
+ * A convenience method to get a test [SplitInfo] with default values provided. With the default
+ * values it returns an empty [ActivityStack] for the primary and secondary stacks. The default
+ * [SplitAttributes] are for splitting equally and matching the locale layout.
+ *
+ * Note: This method should be used for testing local logic as opposed to end to end verification.
+ * End to end verification requires a device that supports Activity Embedding.
+ *
+ * @param primaryActivity the [Activity] for the primary container.
+ * @param secondaryActivity the [Activity] for the secondary container.
+ * @param splitAttributes the [SplitAttributes].
+ */
+@ExperimentalWindowApi
+fun TestSplitInfo(
+ primaryActivity: Activity,
+ secondaryActivity: Activity,
+ splitAttributes: SplitAttributes = SplitAttributes(),
+ token: IBinder = Binder()
+): SplitInfo {
+ val primaryActivityStack = TestActivityStack(primaryActivity, false)
+ val secondaryActivityStack = TestActivityStack(secondaryActivity, false)
+ return SplitInfo(primaryActivityStack, secondaryActivityStack, splitAttributes, token)
+}
+
+/**
+ * A convenience method to get a test [ActivityStack] with default values provided. With the default
+ * values, there will be a single [Activity] in the stack and it will be considered not empty.
+ *
+ * Note: This method should be used for testing local logic as opposed to end to end verification.
+ * End to end verification requires a device that supports Activity Embedding.
+ *
+ * @param testActivity an [Activity] that should be considered in the stack
+ * @param isEmpty states if the stack is empty or not. In practice an [ActivityStack] with a single
+ * [Activity] but [isEmpty] set to `false` means there is an [Activity] from outside the process
+ * in the stack.
+ */
+@ExperimentalWindowApi
+fun TestActivityStack(
+ testActivity: Activity,
+ isEmpty: Boolean = true,
+ token: IBinder = Binder()
+): ActivityStack {
+ return ActivityStack(
+ listOf(testActivity),
+ isEmpty,
+ token
+ )
+}
\ No newline at end of file
diff --git a/window/window-testing/src/test/resources/robolectric.properties b/window/window-testing/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/window/window-testing/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/window/window/api/api_lint.ignore b/window/window/api/api_lint.ignore
index 24bf744..d427dda 100644
--- a/window/window/api/api_lint.ignore
+++ b/window/window/api/api_lint.ignore
@@ -1,4 +1,6 @@
// Baseline format: 1.0
+ContextFirst: androidx.window.embedding.ActivityEmbeddingOptions#setLaunchingActivityStack(android.app.ActivityOptions, android.content.Context, androidx.window.embedding.ActivityStack) parameter #1:
+ Context is distinct, so it must be the first argument (method `windowLayoutInfoFlowable`)
GetterSetterNames: field ActivityRule.alwaysExpand:
Invalid name for boolean property `alwaysExpand`. Should start with one of `has`, `can`, `should`, `is`.
GetterSetterNames: field SplitAttributesCalculatorParams.areDefaultConstraintsSatisfied:
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 412af2b..e28ac35 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -5,6 +5,94 @@
field public static final androidx.window.WindowProperties INSTANCE;
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+ field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
+ field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
+ field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+ }
+
+}
+
+package androidx.window.area {
+
+ public final class WindowAreaCapability {
+ method public androidx.window.area.WindowAreaCapability.Operation getOperation();
+ method public androidx.window.area.WindowAreaCapability.Status getStatus();
+ property public final androidx.window.area.WindowAreaCapability.Operation operation;
+ property public final androidx.window.area.WindowAreaCapability.Status status;
+ }
+
+ public static final class WindowAreaCapability.Operation {
+ field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
+ field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
+ field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
+ }
+
+ public static final class WindowAreaCapability.Operation.Companion {
+ }
+
+ public static final class WindowAreaCapability.Status {
+ field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNAVAILABLE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNSUPPORTED;
+ }
+
+ public static final class WindowAreaCapability.Status.Companion {
+ }
+
+ public interface WindowAreaController {
+ method public default static androidx.window.area.WindowAreaController getOrCreate();
+ method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
+ method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
+ method public void transferActivityToWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaSessionCallback windowAreaSessionCallback);
+ property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> windowAreaInfos;
+ field public static final androidx.window.area.WindowAreaController.Companion Companion;
+ }
+
+ public static final class WindowAreaController.Companion {
+ method public androidx.window.area.WindowAreaController getOrCreate();
+ }
+
+ public final class WindowAreaInfo {
+ method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
+ method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
+ method public androidx.window.layout.WindowMetrics getMetrics();
+ method public android.os.Binder getToken();
+ method public androidx.window.area.WindowAreaInfo.Type getType();
+ method public void setMetrics(androidx.window.layout.WindowMetrics);
+ property public final androidx.window.layout.WindowMetrics metrics;
+ property public final android.os.Binder token;
+ property public final androidx.window.area.WindowAreaInfo.Type type;
+ }
+
+ public static final class WindowAreaInfo.Type {
+ field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
+ field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
+ }
+
+ public static final class WindowAreaInfo.Type.Companion {
+ }
+
+ public interface WindowAreaPresentationSessionCallback {
+ method public void onContainerVisibilityChanged(boolean isVisible);
+ method public void onSessionEnded(Throwable? t);
+ method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
+ }
+
+ public interface WindowAreaSession {
+ method public void close();
+ }
+
+ public interface WindowAreaSessionCallback {
+ method public void onSessionEnded(Throwable? t);
+ method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
+ }
+
+ public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+ method public android.content.Context getContext();
+ method public void setContentView(android.view.View view);
+ property public abstract android.content.Context context;
}
}
@@ -19,8 +107,11 @@
package androidx.window.embedding {
public final class ActivityEmbeddingController {
+ method @androidx.window.core.ExperimentalWindowApi public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+ method @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
method public boolean isActivityEmbedded(android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public boolean isFinishingActivityStacksSupported();
field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
}
@@ -28,6 +119,12 @@
method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
}
+ public final class ActivityEmbeddingOptions {
+ method @androidx.window.core.ExperimentalWindowApi public static boolean isSetLaunchingActivityStackSupported(android.app.ActivityOptions);
+ method @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+ }
+
public final class ActivityFilter {
ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
method public android.content.ComponentName getComponentName();
@@ -132,7 +229,7 @@
method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
}
- @androidx.window.core.ExperimentalWindowApi public final class SplitAttributesCalculatorParams {
+ public final class SplitAttributesCalculatorParams {
method public boolean getAreDefaultConstraintsSatisfied();
method public androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
method public android.content.res.Configuration getParentConfiguration();
@@ -149,14 +246,18 @@
public final class SplitController {
method @Deprecated @androidx.window.core.ExperimentalWindowApi public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
- method @androidx.window.core.ExperimentalWindowApi public void clearSplitAttributesCalculator();
+ method public void clearSplitAttributesCalculator();
method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
- method @androidx.window.core.ExperimentalWindowApi public boolean isSplitAttributesCalculatorSupported();
+ method @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+ method @androidx.window.core.ExperimentalWindowApi public boolean isInvalidatingTopVisibleSplitAttributesSupported();
+ method public boolean isSplitAttributesCalculatorSupported();
method @Deprecated @androidx.window.core.ExperimentalWindowApi public boolean isSplitSupported();
+ method @androidx.window.core.ExperimentalWindowApi public boolean isUpdatingSplitAttributesSupported();
method @Deprecated @androidx.window.core.ExperimentalWindowApi public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
- method @androidx.window.core.ExperimentalWindowApi public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
+ method public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
field public static final androidx.window.embedding.SplitController.Companion Companion;
}
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 412af2b..e28ac35 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -5,6 +5,94 @@
field public static final androidx.window.WindowProperties INSTANCE;
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+ field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
+ field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
+ field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+ }
+
+}
+
+package androidx.window.area {
+
+ public final class WindowAreaCapability {
+ method public androidx.window.area.WindowAreaCapability.Operation getOperation();
+ method public androidx.window.area.WindowAreaCapability.Status getStatus();
+ property public final androidx.window.area.WindowAreaCapability.Operation operation;
+ property public final androidx.window.area.WindowAreaCapability.Status status;
+ }
+
+ public static final class WindowAreaCapability.Operation {
+ field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
+ field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
+ field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
+ }
+
+ public static final class WindowAreaCapability.Operation.Companion {
+ }
+
+ public static final class WindowAreaCapability.Status {
+ field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNAVAILABLE;
+ field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNSUPPORTED;
+ }
+
+ public static final class WindowAreaCapability.Status.Companion {
+ }
+
+ public interface WindowAreaController {
+ method public default static androidx.window.area.WindowAreaController getOrCreate();
+ method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
+ method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
+ method public void transferActivityToWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaSessionCallback windowAreaSessionCallback);
+ property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> windowAreaInfos;
+ field public static final androidx.window.area.WindowAreaController.Companion Companion;
+ }
+
+ public static final class WindowAreaController.Companion {
+ method public androidx.window.area.WindowAreaController getOrCreate();
+ }
+
+ public final class WindowAreaInfo {
+ method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
+ method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
+ method public androidx.window.layout.WindowMetrics getMetrics();
+ method public android.os.Binder getToken();
+ method public androidx.window.area.WindowAreaInfo.Type getType();
+ method public void setMetrics(androidx.window.layout.WindowMetrics);
+ property public final androidx.window.layout.WindowMetrics metrics;
+ property public final android.os.Binder token;
+ property public final androidx.window.area.WindowAreaInfo.Type type;
+ }
+
+ public static final class WindowAreaInfo.Type {
+ field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
+ field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
+ }
+
+ public static final class WindowAreaInfo.Type.Companion {
+ }
+
+ public interface WindowAreaPresentationSessionCallback {
+ method public void onContainerVisibilityChanged(boolean isVisible);
+ method public void onSessionEnded(Throwable? t);
+ method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
+ }
+
+ public interface WindowAreaSession {
+ method public void close();
+ }
+
+ public interface WindowAreaSessionCallback {
+ method public void onSessionEnded(Throwable? t);
+ method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
+ }
+
+ public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+ method public android.content.Context getContext();
+ method public void setContentView(android.view.View view);
+ property public abstract android.content.Context context;
}
}
@@ -19,8 +107,11 @@
package androidx.window.embedding {
public final class ActivityEmbeddingController {
+ method @androidx.window.core.ExperimentalWindowApi public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+ method @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
method public boolean isActivityEmbedded(android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public boolean isFinishingActivityStacksSupported();
field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
}
@@ -28,6 +119,12 @@
method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
}
+ public final class ActivityEmbeddingOptions {
+ method @androidx.window.core.ExperimentalWindowApi public static boolean isSetLaunchingActivityStackSupported(android.app.ActivityOptions);
+ method @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+ }
+
public final class ActivityFilter {
ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
method public android.content.ComponentName getComponentName();
@@ -132,7 +229,7 @@
method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
}
- @androidx.window.core.ExperimentalWindowApi public final class SplitAttributesCalculatorParams {
+ public final class SplitAttributesCalculatorParams {
method public boolean getAreDefaultConstraintsSatisfied();
method public androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
method public android.content.res.Configuration getParentConfiguration();
@@ -149,14 +246,18 @@
public final class SplitController {
method @Deprecated @androidx.window.core.ExperimentalWindowApi public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
- method @androidx.window.core.ExperimentalWindowApi public void clearSplitAttributesCalculator();
+ method public void clearSplitAttributesCalculator();
method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
- method @androidx.window.core.ExperimentalWindowApi public boolean isSplitAttributesCalculatorSupported();
+ method @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+ method @androidx.window.core.ExperimentalWindowApi public boolean isInvalidatingTopVisibleSplitAttributesSupported();
+ method public boolean isSplitAttributesCalculatorSupported();
method @Deprecated @androidx.window.core.ExperimentalWindowApi public boolean isSplitSupported();
+ method @androidx.window.core.ExperimentalWindowApi public boolean isUpdatingSplitAttributesSupported();
method @Deprecated @androidx.window.core.ExperimentalWindowApi public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
- method @androidx.window.core.ExperimentalWindowApi public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
+ method public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+ method @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
field public static final androidx.window.embedding.SplitController.Companion Companion;
}
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 402be78..ba28888a 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -49,11 +49,9 @@
implementation("androidx.collection:collection:1.1.0")
implementation("androidx.core:core:1.8.0")
- def extensions_core_version = "androidx.window.extensions.core:core:1.0.0-rc01"
- def extensions_version = "androidx.window.extensions:extensions:1.1.0-rc01"
- implementation(extensions_core_version)
+ implementation("androidx.window.extensions.core:core:1.0.0-beta01")
compileOnly(project(":window:sidecar:sidecar"))
- compileOnly(extensions_version)
+ compileOnly(project(":window:extensions:extensions"))
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
@@ -63,9 +61,9 @@
testImplementation(libs.mockitoCore4)
testImplementation(libs.mockitoKotlin4)
testImplementation(libs.kotlinCoroutinesTest)
- testImplementation(extensions_core_version)
+ testImplementation("androidx.window.extensions.core:core:1.0.0-beta01")
testImplementation(compileOnly(project(":window:sidecar:sidecar")))
- testImplementation(compileOnly(extensions_version))
+ testImplementation(compileOnly(project(":window:extensions:extensions")))
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.kotlinTestJunit)
@@ -79,9 +77,9 @@
androidTestImplementation(libs.multidex)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.junit) // Needed for Assert.assertThrows
- androidTestImplementation(extensions_core_version)
+ androidTestImplementation("androidx.window.extensions.core:core:1.0.0-beta01")
androidTestImplementation(compileOnly(project(":window:sidecar:sidecar")))
- androidTestImplementation(compileOnly(extensions_version))
+ androidTestImplementation(compileOnly(project(":window:extensions:extensions")))
}
androidx {
diff --git a/window/window/proguard-rules.pro b/window/window/proguard-rules.pro
index 609e1cc1..b8cf236 100644
--- a/window/window/proguard-rules.pro
+++ b/window/window/proguard-rules.pro
@@ -22,4 +22,6 @@
androidx.window.layout.adapter.sidecar.DistinctElementSidecarCallback {
public *** onDeviceStateChanged(androidx.window.sidecar.SidecarDeviceState);
public *** onWindowLayoutChanged(android.os.IBinder, androidx.window.sidecar.SidecarWindowLayoutInfo);
-}
\ No newline at end of file
+}
+# Required for window area API reflection guard
+-keep interface androidx.window.area.reflectionguard.* {*;}
\ No newline at end of file
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt
new file mode 100644
index 0000000..a7ab9cb
--- /dev/null
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.samples.embedding
+
+import android.app.Activity
+import androidx.annotation.Sampled
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.embedding.ActivityEmbeddingController
+import androidx.window.embedding.SplitController
+
+@OptIn(ExperimentalWindowApi::class)
+@Sampled
+suspend fun expandPrimaryContainer() {
+ SplitController.getInstance(primaryActivity).splitInfoList(primaryActivity)
+ .collect { splitInfoList ->
+ // Find all associated secondary ActivityStacks
+ val associatedSecondaryActivityStacks = splitInfoList
+ .mapTo(mutableSetOf()) { splitInfo -> splitInfo.secondaryActivityStack }
+ // Finish them all.
+ ActivityEmbeddingController.getInstance(primaryActivity)
+ .finishActivityStacks(associatedSecondaryActivityStacks)
+ }
+}
+
+val primaryActivity = Activity()
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
index c6a3607..e244fb9 100644
--- a/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
@@ -18,7 +18,6 @@
import android.app.Application
import androidx.annotation.Sampled
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.SplitAttributes
import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EQUAL
import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EXPAND
@@ -26,7 +25,6 @@
import androidx.window.embedding.SplitController
import androidx.window.layout.FoldingFeature
-@OptIn(ExperimentalWindowApi::class)
@Sampled
fun splitAttributesCalculatorSample() {
SplitController.getInstance(context)
@@ -79,7 +77,6 @@
}
}
-@OptIn(ExperimentalWindowApi::class)
@Sampled
fun splitWithOrientations() {
SplitController.getInstance(context)
@@ -107,7 +104,6 @@
}
}
-@OptIn(ExperimentalWindowApi::class)
@Sampled
fun expandContainersInPortrait() {
SplitController.getInstance(context)
@@ -135,7 +131,6 @@
}
}
-@OptIn(ExperimentalWindowApi::class)
@Sampled
fun fallbackToExpandContainersForSplitTypeHinge() {
SplitController.getInstance(context).setSplitAttributesCalculator { params ->
diff --git a/window/window/src/androidTest/AndroidManifest.xml b/window/window/src/androidTest/AndroidManifest.xml
index cd1d81e..e0d60f16 100644
--- a/window/window/src/androidTest/AndroidManifest.xml
+++ b/window/window/src/androidTest/AndroidManifest.xml
@@ -28,5 +28,19 @@
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
android:value="true" />
+
+ <!-- Compat property -->
+ <property
+ android:name=
+ "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
+ android:value="false" />
+ <property
+ android:name=
+ "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"
+ android:value="false" />
+ <property
+ android:name=
+ "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
+ android:value="false" />
</application>
</manifest>
diff --git a/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt b/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
index 34eb50f..57f5d51 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
@@ -21,6 +21,7 @@
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeTrue
import org.junit.Rule
@@ -67,6 +68,62 @@
}
}
+ @Test
+ fun test_property_allow_ignoring_orientation_request_when_loop_detected() {
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ // No-op, but to suppress lint
+ return
+ }
+ activityRule.scenario.onActivity { activity ->
+ // Should be false as defined in AndroidManifest.xml
+ assertFalse(
+ getProperty(
+ activity,
+ WindowProperties
+ .PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED
+ )
+ )
+ }
+ }
+
+ @Test
+ fun test_property_allow_min_aspect_ratio_override() {
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ // No-op, but to suppress lint
+ return
+ }
+ activityRule.scenario.onActivity { activity ->
+ // Should be false as defined in AndroidManifest.xml
+ assertFalse(
+ getProperty(
+ activity,
+ WindowProperties.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE
+ )
+ )
+ }
+ }
+
+ @Test
+ fun test_property_allow_resizeable_activity_overrides() {
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ // No-op, but to suppress lint
+ return
+ }
+ activityRule.scenario.onActivity { activity ->
+ // Should be false as defined in AndroidManifest.xml
+ assertFalse(
+ getProperty(
+ activity,
+ WindowProperties
+ .PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES
+ )
+ )
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.S)
@Throws(PackageManager.NameNotFoundException::class)
private fun getProperty(context: Context, propertyName: String): Boolean {
diff --git a/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt
new file mode 100644
index 0000000..50bb347
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import androidx.window.core.ExtensionsUtil
+import androidx.window.extensions.WindowExtensionsProvider
+import org.junit.Assert.assertNull
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+
+/**
+ * An integration test to verify that if [WindowExtensionsProvider] is present then
+ * [SafeWindowAreaComponentProvider.windowAreaComponent] will return a value. This can fail if
+ * the implementation of window:extensions:extensions does not have the expected API.
+ */
+class SafeWindowAreaComponentProviderTest {
+
+ /**
+ * Test that if [WindowExtensionsProvider] is available then
+ * [SafeWindowAreaComponentProvider.windowAreaComponent] returns a non-null value.
+ */
+ @Test
+ fun windowAreaComponentIsAvailable_ifProviderIsAvailable() {
+ assumeTrue(ExtensionsUtil.safeVendorApiLevel >= 2)
+ val loader = SafeWindowAreaComponentProvider::class.java.classLoader!!
+ val safeComponent = SafeWindowAreaComponentProvider(loader).windowAreaComponent
+
+ try {
+ val extensions = WindowExtensionsProvider.getWindowExtensions()
+ val actualComponent = extensions.windowAreaComponent
+ if (actualComponent == null) {
+ assertNull(safeComponent)
+ }
+ // TODO(b/267831038): verify upon each api level
+ // TODO(b/267708462): more reliable test for testing actual method matching
+ } catch (e: UnsupportedOperationException) {
+ // Invalid implementation of extensions
+ assertNull(safeComponent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
index 963966d..f6f80437 100644
--- a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
@@ -16,29 +16,49 @@
package androidx.window.area
-import android.annotation.TargetApi
import android.app.Activity
+import android.content.Context
import android.content.pm.ActivityInfo
+import android.os.Binder
import android.os.Build
+import android.util.DisplayMetrics
+import android.view.View
+import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.window.TestActivity
-import androidx.window.TestConsumer
import androidx.window.WindowTestUtils.Companion.assumeAtLeastVendorApiLevel
-import androidx.window.core.ExperimentalWindowApi
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_PRESENT_ON_AREA
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation
+import androidx.window.extensions.area.ExtensionWindowAreaStatus
import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_INACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_ACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_AVAILABLE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_UNAVAILABLE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_UNSUPPORTED
import androidx.window.extensions.core.util.function.Consumer
-import kotlin.test.assertFailsWith
+import androidx.window.layout.WindowMetricsCalculator
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalWindowApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class WindowAreaControllerImplTest {
@get:Rule
@@ -47,55 +67,121 @@
private val testScope = TestScope(UnconfinedTestDispatcher())
- @TargetApi(Build.VERSION_CODES.N)
+ /**
+ * Tests that we can get a list of [WindowAreaInfo] objects with a type of
+ * [WindowAreaInfo.Type.TYPE_REAR_FACING]. Verifies that updating the status of features on
+ * device returns an updated [WindowAreaInfo] list.
+ */
+ @RequiresApi(Build.VERSION_CODES.Q)
@Test
- public fun testRearDisplayStatus(): Unit = testScope.runTest {
- assumeAtLeastVendorApiLevel(2)
+ public fun testRearFacingWindowAreaInfoList(): Unit = testScope.runTest {
+ assumeTrue(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q)
+ assumeAtLeastVendorApiLevel(3)
activityScenario.scenario.onActivity {
val extensionComponent = FakeWindowAreaComponent()
- val repo = WindowAreaControllerImpl(extensionComponent)
- val collector = TestConsumer<WindowAreaStatus>()
- extensionComponent
- .updateStatusListeners(WindowAreaComponent.STATUS_UNAVAILABLE)
- testScope.launch(Job()) {
- repo.rearDisplayStatus().collect(collector::accept)
- }
- collector.assertValue(WindowAreaStatus.UNAVAILABLE)
- extensionComponent
- .updateStatusListeners(WindowAreaComponent.STATUS_AVAILABLE)
- collector.assertValues(
- WindowAreaStatus.UNAVAILABLE,
- WindowAreaStatus.AVAILABLE
+ val controller = WindowAreaControllerImpl(
+ windowAreaComponent = extensionComponent,
+ vendorApiLevel = 3
)
+ extensionComponent.currentRearDisplayStatus = STATUS_UNAVAILABLE
+ extensionComponent.currentRearDisplayPresentationStatus = STATUS_UNAVAILABLE
+ val collector = TestWindowAreaInfoListConsumer()
+ testScope.launch(Job()) {
+ controller.windowAreaInfos.collect(collector::accept)
+ }
+
+ val expectedAreaInfo = WindowAreaInfo(
+ metrics = WindowMetricsCalculator.fromDisplayMetrics(
+ extensionComponent.rearDisplayMetrics
+ ),
+ type = WindowAreaInfo.Type.TYPE_REAR_FACING,
+ token = Binder(REAR_FACING_BINDER_DESCRIPTION),
+ windowAreaComponent = extensionComponent
+ )
+ val rearDisplayCapability = WindowAreaCapability(
+ OPERATION_TRANSFER_ACTIVITY_TO_AREA,
+ WINDOW_AREA_STATUS_UNAVAILABLE
+ )
+ val rearDisplayPresentationCapability = WindowAreaCapability(
+ OPERATION_PRESENT_ON_AREA,
+ WINDOW_AREA_STATUS_UNAVAILABLE
+ )
+ expectedAreaInfo.capabilityMap[OPERATION_TRANSFER_ACTIVITY_TO_AREA] =
+ rearDisplayCapability
+ expectedAreaInfo.capabilityMap[OPERATION_PRESENT_ON_AREA] =
+ rearDisplayPresentationCapability
+
+ assertEquals(listOf(expectedAreaInfo), collector.values[collector.values.size - 1])
+
+ extensionComponent
+ .updateRearDisplayStatusListeners(STATUS_AVAILABLE)
+
+ val updatedRearDisplayCapability = WindowAreaCapability(
+ OPERATION_TRANSFER_ACTIVITY_TO_AREA,
+ WINDOW_AREA_STATUS_AVAILABLE
+ )
+ expectedAreaInfo.capabilityMap[OPERATION_TRANSFER_ACTIVITY_TO_AREA] =
+ updatedRearDisplayCapability
+
+ assertEquals(listOf(expectedAreaInfo), collector.values[collector.values.size - 1])
+
+ // Update the presentation capability status and verify that only one window area
+ // info is still returned
+ extensionComponent.updateRearDisplayPresentationStatusListeners(STATUS_AVAILABLE)
+
+ val updatedRearDisplayPresentationCapability = WindowAreaCapability(
+ OPERATION_PRESENT_ON_AREA,
+ WINDOW_AREA_STATUS_AVAILABLE
+ )
+ expectedAreaInfo.capabilityMap[OPERATION_PRESENT_ON_AREA] =
+ updatedRearDisplayPresentationCapability
+
+ assertEquals(listOf(expectedAreaInfo), collector.values[collector.values.size - 1])
}
}
@Test
- public fun testRearDisplayStatusNullComponent(): Unit = testScope.runTest {
+ public fun testWindowAreaInfoListNullComponent(): Unit = testScope.runTest {
activityScenario.scenario.onActivity {
- val repo = EmptyWindowAreaControllerImpl()
- val collector = TestConsumer<WindowAreaStatus>()
+ val controller = EmptyWindowAreaControllerImpl()
+ val collector = TestWindowAreaInfoListConsumer()
testScope.launch(Job()) {
- repo.rearDisplayStatus().collect(collector::accept)
+ controller.windowAreaInfos.collect(collector::accept)
}
- collector.assertValue(WindowAreaStatus.UNSUPPORTED)
+ assertTrue(collector.values.size == 1)
+ assertEquals(listOf(), collector.values[0])
}
}
/**
- * Tests the rear display mode flow works as expected. Tests the flow
+ * Tests the transfer to rear facing window area flow. Tests the flow
* through WindowAreaControllerImpl with a fake extension. This fake extension
- * changes the orientation of the activity to landscape when rear display mode is enabled
- * and then returns it back to portrait when it's disabled.
+ * changes the orientation of the activity to landscape to simulate a configuration change that
+ * would occur when transferring to the rear facing window area and then returns it back to
+ * portrait when it's disabled.
*/
- @TargetApi(Build.VERSION_CODES.N)
+ @RequiresApi(Build.VERSION_CODES.Q)
@Test
- public fun testRearDisplayMode(): Unit = testScope.runTest {
- assumeAtLeastVendorApiLevel(2)
+ public fun testTransferToRearFacingWindowArea(): Unit = testScope.runTest {
+ assumeAtLeastVendorApiLevel(3)
val extensions = FakeWindowAreaComponent()
- val repo = WindowAreaControllerImpl(extensions)
- extensions.currentStatus = WindowAreaComponent.STATUS_AVAILABLE
+ val controller = WindowAreaControllerImpl(
+ windowAreaComponent = extensions,
+ vendorApiLevel = 3
+ )
+ extensions.currentRearDisplayStatus = STATUS_AVAILABLE
val callback = TestWindowAreaSessionCallback()
+ var windowAreaInfo: WindowAreaInfo? = null
+ testScope.launch(Job()) {
+ windowAreaInfo = controller.windowAreaInfos.firstOrNull()
+ ?.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING }
+ }
+ assertNotNull(windowAreaInfo)
+ assertEquals(
+ windowAreaInfo!!.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA)?.status,
+ WINDOW_AREA_STATUS_AVAILABLE
+ )
+
activityScenario.scenario.onActivity { testActivity ->
testActivity.resetLayoutCounter()
testActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
@@ -105,7 +191,12 @@
activityScenario.scenario.onActivity { testActivity ->
assert(testActivity.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
testActivity.resetLayoutCounter()
- repo.rearDisplayMode(testActivity, Runnable::run, callback)
+ controller.transferActivityToWindowArea(
+ windowAreaInfo!!.token,
+ testActivity,
+ Runnable::run,
+ callback
+ )
}
activityScenario.scenario.onActivity { testActivity ->
@@ -120,84 +211,255 @@
}
}
- @TargetApi(Build.VERSION_CODES.N)
+ @RequiresApi(Build.VERSION_CODES.Q)
@Test
- public fun testRearDisplayModeReturnsError(): Unit = testScope.runTest {
- assumeAtLeastVendorApiLevel(2)
- val extensionComponent = FakeWindowAreaComponent()
- extensionComponent.currentStatus = WindowAreaComponent.STATUS_UNAVAILABLE
- val repo = WindowAreaControllerImpl(extensionComponent)
+ public fun testTransferRearDisplayReturnsError_statusUnavailable(): Unit = testScope.runTest {
+ testTransferRearDisplayReturnsError(STATUS_UNAVAILABLE)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ @Test
+ public fun testTransferRearDisplayReturnsError_statusActive(): Unit = testScope.runTest {
+ testTransferRearDisplayReturnsError(STATUS_ACTIVE)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ private fun testTransferRearDisplayReturnsError(
+ initialState: @WindowAreaComponent.WindowAreaStatus Int
+ ) {
+ assumeAtLeastVendorApiLevel(3)
+ val extensions = FakeWindowAreaComponent()
+ val controller = WindowAreaControllerImpl(
+ windowAreaComponent = extensions,
+ vendorApiLevel = 2
+ )
+ extensions.currentRearDisplayStatus = initialState
val callback = TestWindowAreaSessionCallback()
+ var windowAreaInfo: WindowAreaInfo? = null
+ testScope.launch(Job()) {
+ windowAreaInfo = controller.windowAreaInfos.firstOrNull()
+ ?.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING }
+ }
+ assertNotNull(windowAreaInfo)
+ assertEquals(
+ windowAreaInfo!!.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA)?.status,
+ WindowAreaAdapter.translate(initialState)
+ )
+
activityScenario.scenario.onActivity { testActivity ->
- assertFailsWith(
- exceptionClass = UnsupportedOperationException::class,
- block = { repo.rearDisplayMode(testActivity, Runnable::run, callback) }
+ controller.transferActivityToWindowArea(
+ windowAreaInfo!!.token,
+ testActivity,
+ Runnable::run,
+ callback
)
+ assertNotNull(callback.error)
+ assertNull(callback.currentSession)
}
}
- @TargetApi(Build.VERSION_CODES.N)
+ /**
+ * Tests the presentation flow on to a rear facing display works as expected. The
+ * [WindowAreaPresentationSessionCallback] provided to
+ * [WindowAreaControllerImpl.presentContentOnWindowArea] should receive a
+ * [WindowAreaSessionPresenter] when the session is active, and be notified that the [View]
+ * provided through [WindowAreaSessionPresenter.setContentView] is visible when inflated.
+ *
+ * Tests the flow through WindowAreaControllerImpl with a fake extension component.
+ */
+ @RequiresApi(Build.VERSION_CODES.Q)
@Test
- public fun testRearDisplayModeNullComponent(): Unit = testScope.runTest {
- val repo = EmptyWindowAreaControllerImpl()
- val callback = TestWindowAreaSessionCallback()
+ public fun testPresentRearDisplayArea(): Unit = testScope.runTest {
+ assumeAtLeastVendorApiLevel(3)
+ val extensions = FakeWindowAreaComponent()
+ val controller = WindowAreaControllerImpl(
+ windowAreaComponent = extensions,
+ vendorApiLevel = 3
+ )
+ var windowAreaInfo: WindowAreaInfo? = null
+ extensions.updateRearDisplayStatusListeners(STATUS_AVAILABLE)
+ extensions.updateRearDisplayPresentationStatusListeners(STATUS_AVAILABLE)
+ testScope.launch(Job()) {
+ windowAreaInfo = controller.windowAreaInfos.firstOrNull()
+ ?.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING }
+ }
+ assertNotNull(windowAreaInfo)
+ assertTrue {
+ windowAreaInfo!!
+ .getCapability(OPERATION_PRESENT_ON_AREA)?.status ==
+ WINDOW_AREA_STATUS_AVAILABLE
+ }
+
+ val callback = TestWindowAreaPresentationSessionCallback()
activityScenario.scenario.onActivity { testActivity ->
- assertFailsWith(
- exceptionClass = UnsupportedOperationException::class,
- block = { repo.rearDisplayMode(testActivity, Runnable::run, callback) }
+ controller.presentContentOnWindowArea(
+ windowAreaInfo!!.token,
+ testActivity,
+ Runnable::run,
+ callback
)
+ assert(callback.sessionActive)
+ assert(!callback.contentVisible)
+
+ callback.presentation?.setContentView(TextView(testActivity))
+ assert(callback.contentVisible)
+ assert(callback.sessionActive)
+
+ callback.presentation?.close()
+ assert(!callback.contentVisible)
+ assert(!callback.sessionActive)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ @Test
+ public fun testRearDisplayPresentationModeSessionEndedError(): Unit = testScope.runTest {
+ assumeAtLeastVendorApiLevel(3)
+ val extensionComponent = FakeWindowAreaComponent()
+ val controller = WindowAreaControllerImpl(
+ windowAreaComponent = extensionComponent,
+ vendorApiLevel = 3
+ )
+ var windowAreaInfo: WindowAreaInfo? = null
+ extensionComponent.updateRearDisplayStatusListeners(STATUS_AVAILABLE)
+ extensionComponent.updateRearDisplayPresentationStatusListeners(STATUS_UNAVAILABLE)
+ testScope.launch(Job()) {
+ windowAreaInfo = controller.windowAreaInfos.firstOrNull()
+ ?.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING }
+ }
+ assertNotNull(windowAreaInfo)
+ assertTrue {
+ windowAreaInfo!!
+ .getCapability(OPERATION_PRESENT_ON_AREA)?.status ==
+ WINDOW_AREA_STATUS_UNAVAILABLE
+ }
+
+ val callback = TestWindowAreaPresentationSessionCallback()
+ activityScenario.scenario.onActivity { testActivity ->
+ controller.presentContentOnWindowArea(
+ windowAreaInfo!!.token,
+ testActivity,
+ Runnable::run,
+ callback
+ )
+ assert(!callback.sessionActive)
+ assert(callback.sessionError != null)
+ assert(callback.sessionError is IllegalStateException)
+ }
+ }
+
+ private class TestWindowAreaInfoListConsumer : Consumer<List<WindowAreaInfo>> {
+
+ val values: MutableList<List<WindowAreaInfo>> = mutableListOf()
+ override fun accept(infos: List<WindowAreaInfo>) {
+ values.add(infos)
}
}
private class FakeWindowAreaComponent : WindowAreaComponent {
- val statusListeners = mutableListOf<Consumer<Int>>()
- var currentStatus = WindowAreaComponent.STATUS_UNSUPPORTED
- var testActivity: Activity? = null
- var sessionConsumer: Consumer<Int>? = null
+ val rearDisplayStatusListeners = mutableListOf<Consumer<Int>>()
+ val rearDisplayPresentationStatusListeners =
+ mutableListOf<Consumer<ExtensionWindowAreaStatus>>()
+ var currentRearDisplayStatus = STATUS_UNSUPPORTED
+ var currentRearDisplayPresentationStatus = STATUS_UNSUPPORTED
- @RequiresApi(Build.VERSION_CODES.N)
+ var testActivity: Activity? = null
+ var rearDisplaySessionConsumer: Consumer<Int>? = null
+ var rearDisplayPresentationSessionConsumer: Consumer<Int>? = null
+
override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
- statusListeners.add(consumer)
- consumer.accept(currentStatus)
+ rearDisplayStatusListeners.add(consumer)
+ consumer.accept(currentRearDisplayStatus)
}
override fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
- statusListeners.remove(consumer)
+ rearDisplayStatusListeners.remove(consumer)
+ }
+
+ override fun addRearDisplayPresentationStatusListener(
+ consumer: Consumer<ExtensionWindowAreaStatus>
+ ) {
+ rearDisplayPresentationStatusListeners.add(consumer)
+ consumer.accept(TestExtensionWindowAreaStatus(currentRearDisplayPresentationStatus))
+ }
+
+ override fun removeRearDisplayPresentationStatusListener(
+ consumer: Consumer<ExtensionWindowAreaStatus>
+ ) {
+ rearDisplayPresentationStatusListeners.remove(consumer)
}
// Fake WindowAreaComponent will change the orientation of the activity to signal
// entering rear display mode, as well as ending the session
- @RequiresApi(Build.VERSION_CODES.N)
override fun startRearDisplaySession(
activity: Activity,
rearDisplaySessionConsumer: Consumer<Int>
) {
- if (currentStatus != WindowAreaComponent.STATUS_AVAILABLE) {
- throw UnsupportedOperationException("Rear Display mode cannot be enabled currently")
+ if (currentRearDisplayStatus != STATUS_AVAILABLE) {
+ rearDisplaySessionConsumer.accept(SESSION_STATE_INACTIVE)
}
testActivity = activity
- sessionConsumer = rearDisplaySessionConsumer
+ this.rearDisplaySessionConsumer = rearDisplaySessionConsumer
testActivity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
rearDisplaySessionConsumer.accept(WindowAreaComponent.SESSION_STATE_ACTIVE)
}
- @RequiresApi(Build.VERSION_CODES.N)
override fun endRearDisplaySession() {
testActivity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- sessionConsumer?.accept(WindowAreaComponent.SESSION_STATE_INACTIVE)
+ rearDisplaySessionConsumer?.accept(SESSION_STATE_INACTIVE)
}
- @RequiresApi(Build.VERSION_CODES.N)
- fun updateStatusListeners(newStatus: Int) {
- currentStatus = newStatus
- for (consumer in statusListeners) {
- consumer.accept(currentStatus)
+ override fun startRearDisplayPresentationSession(
+ activity: Activity,
+ consumer: Consumer<Int>
+ ) {
+ if (currentRearDisplayPresentationStatus != STATUS_AVAILABLE) {
+ consumer.accept(SESSION_STATE_INACTIVE)
+ return
+ }
+ testActivity = activity
+ rearDisplayPresentationSessionConsumer = consumer
+ consumer.accept(SESSION_STATE_ACTIVE)
+ }
+
+ override fun endRearDisplayPresentationSession() {
+ rearDisplayPresentationSessionConsumer?.accept(
+ WindowAreaComponent.SESSION_STATE_ACTIVE)
+ rearDisplayPresentationSessionConsumer?.accept(
+ WindowAreaComponent.SESSION_STATE_INACTIVE)
+ }
+
+ override fun getRearDisplayPresentation(): ExtensionWindowAreaPresentation? {
+ return TestExtensionWindowAreaPresentation(
+ testActivity!!,
+ rearDisplayPresentationSessionConsumer!!
+ )
+ }
+
+ override fun getRearDisplayMetrics(): DisplayMetrics {
+ return DisplayMetrics().apply {
+ widthPixels = 1080
+ heightPixels = 1080
+ densityDpi = 240
+ }
+ }
+
+ fun updateRearDisplayStatusListeners(newStatus: Int) {
+ currentRearDisplayStatus = newStatus
+ for (consumer in rearDisplayStatusListeners) {
+ consumer.accept(currentRearDisplayStatus)
+ }
+ }
+
+ fun updateRearDisplayPresentationStatusListeners(newStatus: Int) {
+ currentRearDisplayPresentationStatus = newStatus
+ for (consumer in rearDisplayPresentationStatusListeners) {
+ consumer.accept(TestExtensionWindowAreaStatus(currentRearDisplayPresentationStatus))
}
}
}
private class TestWindowAreaSessionCallback : WindowAreaSessionCallback {
-
var currentSession: WindowAreaSession? = null
var error: Throwable? = null
@@ -205,10 +467,65 @@
currentSession = session
}
- override fun onSessionEnded() {
+ override fun onSessionEnded(t: Throwable?) {
+ error = t
currentSession = null
}
fun endSession() = currentSession?.close()
}
-}
+
+ private class TestWindowAreaPresentationSessionCallback :
+ WindowAreaPresentationSessionCallback {
+ var sessionActive: Boolean = false
+ var contentVisible: Boolean = false
+ var presentation: WindowAreaSessionPresenter? = null
+ var sessionError: Throwable? = null
+ override fun onSessionStarted(session: WindowAreaSessionPresenter) {
+ sessionActive = true
+ presentation = session
+ }
+
+ override fun onSessionEnded(t: Throwable?) {
+ presentation = null
+ sessionActive = false
+ sessionError = t
+ }
+
+ override fun onContainerVisibilityChanged(isVisible: Boolean) {
+ contentVisible = isVisible
+ }
+ }
+
+ private class TestExtensionWindowAreaStatus(private val status: Int) :
+ ExtensionWindowAreaStatus {
+ override fun getWindowAreaStatus(): Int {
+ return status
+ }
+
+ override fun getWindowAreaDisplayMetrics(): DisplayMetrics {
+ return DisplayMetrics().apply {
+ widthPixels = 1080
+ heightPixels = 1080
+ densityDpi = 240
+ }
+ }
+ }
+
+ private class TestExtensionWindowAreaPresentation(
+ private val activity: Activity,
+ private val sessionConsumer: Consumer<Int>
+ ) : ExtensionWindowAreaPresentation {
+ override fun getPresentationContext(): Context {
+ return activity
+ }
+
+ override fun setPresentationView(view: View) {
+ sessionConsumer.accept(WindowAreaComponent.SESSION_STATE_CONTENT_VISIBLE)
+ }
+ }
+
+ companion object {
+ private const val REAR_FACING_BINDER_DESCRIPTION = "TEST_WINDOW_AREA_REAR_FACING"
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt b/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt
new file mode 100644
index 0000000..532fd65
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard
+
+import android.app.Activity
+import android.content.Context
+import android.util.DisplayMetrics
+import android.view.View
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation
+import androidx.window.extensions.area.ExtensionWindowAreaStatus
+import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.extensions.core.util.function.Consumer
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Test
+
+/**
+ * Unit test for [WindowAreaComponentValidator]
+ */
+class WindowAreaComponentValidatorTest {
+
+ /**
+ * Test that validator returns true if the component fully implements [WindowAreaComponent]
+ */
+ @Test
+ fun isWindowAreaComponentValid_fullImplementation() {
+ assertTrue(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ WindowAreaComponentFullImplementation::class.java, 2))
+ assertTrue(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ WindowAreaComponentFullImplementation::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns correct results for API Level 2 [WindowAreaComponent]
+ * implementation.
+ */
+ @Test
+ fun isWindowAreaComponentValid_apiLevel2() {
+ assertTrue(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ WindowAreaComponentApiV2Implementation::class.java, 2))
+ assertFalse(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ IncompleteWindowAreaComponentApiV2Implementation::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns correct results for API Level 3 [WindowAreaComponent]
+ * implementation.
+ */
+ @Test
+ fun isWindowAreaComponentValid_apiLevel3() {
+ assertTrue(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ WindowAreaComponentApiV3Implementation::class.java, 2))
+ assertTrue(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ WindowAreaComponentApiV3Implementation::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns false if the component implementation is incomplete
+ */
+ @Test
+ fun isWindowAreaComponentValid_falseIfIncompleteImplementation() {
+ assertFalse(
+ WindowAreaComponentValidator.isWindowAreaComponentValid(
+ IncompleteWindowAreaComponentApiV2Implementation::class.java, 2))
+ }
+
+ /**
+ * Test that validator returns true if the [ExtensionWindowAreaStatus] is valid
+ */
+ @Test
+ fun isExtensionWindowAreaStatusValid_trueIfValid() {
+ assertTrue(
+ WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
+ ValidExtensionWindowAreaStatus::class.java, 2))
+ assertTrue(
+ WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
+ ValidExtensionWindowAreaStatus::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns false if the [ExtensionWindowAreaStatus] is incomplete
+ */
+ @Test
+ fun isExtensionWindowAreaStatusValid_falseIfIncomplete() {
+ assertFalse(
+ WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
+ IncompleteExtensionWindowAreaStatus::class.java, 2))
+ assertFalse(
+ WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
+ IncompleteExtensionWindowAreaStatus::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns true if the [ExtensionWindowAreaPresentation] is valid
+ */
+ @Test
+ fun isExtensionWindowAreaPresentationValid_trueIfValid() {
+ assertTrue(
+ WindowAreaComponentValidator.isExtensionWindowAreaPresentationValid(
+ ValidExtensionWindowAreaPresentation::class.java, 3))
+ }
+
+ /**
+ * Test that validator returns false if the [ExtensionWindowAreaPresentation] is incomplete
+ */
+ @Test
+ fun isExtensionWindowAreaPresentationValid_falseIfIncomplete() {
+ assertFalse(
+ WindowAreaComponentValidator.isExtensionWindowAreaPresentationValid(
+ IncompleteExtensionWindowAreaPresentation::class.java, 3))
+ }
+
+ private class WindowAreaComponentFullImplementation : WindowAreaComponent {
+ override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun startRearDisplaySession(activity: Activity, consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun endRearDisplaySession() {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun getRearDisplayMetrics(): DisplayMetrics {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class WindowAreaComponentApiV2Implementation : WindowAreaComponentApi2Requirements {
+ override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun startRearDisplaySession(activity: Activity, consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun endRearDisplaySession() {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class WindowAreaComponentApiV3Implementation : WindowAreaComponentApi3Requirements {
+ override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun startRearDisplaySession(activity: Activity, consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun endRearDisplaySession() {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun addRearDisplayPresentationStatusListener(
+ consumer: Consumer<ExtensionWindowAreaStatus>
+ ) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun removeRearDisplayPresentationStatusListener(
+ consumer: Consumer<ExtensionWindowAreaStatus>
+ ) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun startRearDisplayPresentationSession(
+ activity: Activity,
+ consumer: Consumer<Int>
+ ) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun endRearDisplayPresentationSession() {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun getRearDisplayPresentation(): ExtensionWindowAreaPresentation? {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun getRearDisplayMetrics(): DisplayMetrics {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class IncompleteWindowAreaComponentApiV2Implementation {
+ @Suppress("UNUSED_PARAMETER")
+ fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class ValidExtensionWindowAreaPresentation : ExtensionWindowAreaPresentation {
+ override fun getPresentationContext(): Context {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun setPresentationView(view: View) {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class IncompleteExtensionWindowAreaPresentation {
+ fun getPresentationContext(): Context {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class ValidExtensionWindowAreaStatus : ExtensionWindowAreaStatus {
+ override fun getWindowAreaStatus(): Int {
+ throw NotImplementedError("Not implemented")
+ }
+
+ override fun getWindowAreaDisplayMetrics(): DisplayMetrics {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+
+ private class IncompleteExtensionWindowAreaStatus {
+ fun getWindowAreaStatus(): Int {
+ throw NotImplementedError("Not implemented")
+ }
+ }
+}
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
index 6d76005..5aaf0aa 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
@@ -17,9 +17,13 @@
package androidx.window.embedding
import android.app.Activity
+import android.os.Binder
+import android.os.IBinder
import androidx.window.WindowTestUtils
import androidx.window.core.ExtensionsUtil
import androidx.window.core.PredicateAdapter
+import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_ACTIVITY_STACK_TOKEN
+import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_SPLIT_INFO_TOKEN
import androidx.window.embedding.SplitAttributes.SplitType
import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_HINGE
import androidx.window.extensions.WindowExtensions
@@ -55,12 +59,13 @@
OEMSplitAttributes.Builder().build(),
)
val expectedSplitInfo = SplitInfo(
- ActivityStack(ArrayList(), isEmpty = true),
- ActivityStack(ArrayList(), isEmpty = true),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
SplitAttributes.Builder()
.setSplitType(SplitType.SPLIT_TYPE_EQUAL)
.setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
- .build()
+ .build(),
+ INVALID_SPLIT_INFO_TOKEN,
)
assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
}
@@ -77,12 +82,13 @@
.build(),
)
val expectedSplitInfo = SplitInfo(
- ActivityStack(ArrayList(), isEmpty = true),
- ActivityStack(ArrayList(), isEmpty = true),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
SplitAttributes.Builder()
.setSplitType(SplitType.SPLIT_TYPE_EXPAND)
.setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
- .build()
+ .build(),
+ INVALID_SPLIT_INFO_TOKEN,
)
assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
}
@@ -101,13 +107,14 @@
}
val expectedSplitInfo = SplitInfo(
- ActivityStack(ArrayList(), isEmpty = true),
- ActivityStack(ArrayList(), isEmpty = true),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
SplitAttributes.Builder()
.setSplitType(SplitType.ratio(expectedSplitRatio))
// OEMSplitInfo with Vendor API level 1 doesn't provide layoutDirection.
.setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
- .build()
+ .build(),
+ INVALID_SPLIT_INFO_TOKEN,
)
assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
}
@@ -125,12 +132,39 @@
.build(),
)
val expectedSplitInfo = SplitInfo(
- ActivityStack(ArrayList(), isEmpty = true),
- ActivityStack(ArrayList(), isEmpty = true),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
+ ActivityStack(ArrayList(), isEmpty = true, INVALID_ACTIVITY_STACK_TOKEN),
SplitAttributes.Builder()
.setSplitType(SPLIT_TYPE_HINGE)
.setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
- .build()
+ .build(),
+ INVALID_SPLIT_INFO_TOKEN,
+ )
+ assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+ }
+
+ @Test
+ fun testTranslateSplitInfoWithApiLevel3() {
+ WindowTestUtils.assumeAtLeastVendorApiLevel(WindowExtensions.VENDOR_API_LEVEL_3)
+ val testStackToken = Binder()
+ val testSplitInfoToken = Binder()
+ val oemSplitInfo = createTestOEMSplitInfo(
+ createTestOEMActivityStack(ArrayList(), true, testStackToken),
+ createTestOEMActivityStack(ArrayList(), true, testStackToken),
+ OEMSplitAttributes.Builder()
+ .setSplitType(OEMSplitAttributes.SplitType.HingeSplitType(RatioSplitType(0.5f)))
+ .setLayoutDirection(TOP_TO_BOTTOM)
+ .build(),
+ testSplitInfoToken,
+ )
+ val expectedSplitInfo = SplitInfo(
+ ActivityStack(ArrayList(), isEmpty = true, testStackToken),
+ ActivityStack(ArrayList(), isEmpty = true, testStackToken),
+ SplitAttributes.Builder()
+ .setSplitType(SPLIT_TYPE_HINGE)
+ .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
+ .build(),
+ testSplitInfoToken,
)
assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
}
@@ -139,6 +173,7 @@
testPrimaryActivityStack: OEMActivityStack,
testSecondaryActivityStack: OEMActivityStack,
testSplitAttributes: OEMSplitAttributes,
+ testToken: IBinder = INVALID_SPLIT_INFO_TOKEN,
): OEMSplitInfo {
return mock<OEMSplitInfo>().apply {
whenever(primaryActivityStack).thenReturn(testPrimaryActivityStack)
@@ -146,16 +181,23 @@
if (ExtensionsUtil.safeVendorApiLevel >= WindowExtensions.VENDOR_API_LEVEL_2) {
whenever(splitAttributes).thenReturn(testSplitAttributes)
}
+ if (ExtensionsUtil.safeVendorApiLevel >= WindowExtensions.VENDOR_API_LEVEL_3) {
+ whenever(token).thenReturn(testToken)
+ }
}
}
private fun createTestOEMActivityStack(
testActivities: List<Activity>,
testIsEmpty: Boolean,
+ testToken: IBinder = INVALID_ACTIVITY_STACK_TOKEN,
): OEMActivityStack {
return mock<OEMActivityStack>().apply {
whenever(activities).thenReturn(testActivities)
whenever(isEmpty).thenReturn(testIsEmpty)
+ if (ExtensionsUtil.safeVendorApiLevel >= WindowExtensions.VENDOR_API_LEVEL_3) {
+ whenever(token).thenReturn(testToken)
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
index 2aa82f4..c33b215 100644
--- a/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
+++ b/window/window/src/main/java/androidx/window/SafeWindowExtensionsProvider.kt
@@ -16,12 +16,26 @@
package androidx.window
+import androidx.window.extensions.WindowExtensions
+import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.reflection.ReflectionUtils
import androidx.window.reflection.ReflectionUtils.doesReturn
import androidx.window.reflection.ReflectionUtils.isPublic
import androidx.window.reflection.WindowExtensionsConstants
internal class SafeWindowExtensionsProvider(private val loader: ClassLoader) {
+
+ val windowExtensions: WindowExtensions?
+ get() {
+ return try {
+ if (isWindowExtensionsPresent() && isWindowExtensionsValid()) {
+ WindowExtensionsProvider.getWindowExtensions()
+ } else null
+ } catch (e: Exception) {
+ null
+ }
+ }
+
internal val windowExtensionsClass: Class<*>
get() {
return loader.loadClass(WindowExtensionsConstants.WINDOW_EXTENSIONS_CLASS)
diff --git a/window/window/src/main/java/androidx/window/WindowProperties.kt b/window/window/src/main/java/androidx/window/WindowProperties.kt
index 156652f..6f73640 100644
--- a/window/window/src/main/java/androidx/window/WindowProperties.kt
+++ b/window/window/src/main/java/androidx/window/WindowProperties.kt
@@ -87,4 +87,99 @@
*/
const val PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED =
"android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
+
+ /**
+ * Application level
+ * [PackageManager][android.content.pm.PackageManager.Property] tag
+ * for an app to inform the system that the app can be opted-out from the compatibility
+ * treatment that avoids [android.app.Activity.setRequestedOrientation] loops. The loop
+ * can be trigerred when ignoreOrientationRequest display setting is
+ * enabled on the device (enables compatibility mode for fixed orientation,
+ * see [Enhanced letterboxing](https://developer.android.com/guide/practices/enhanced-letterboxing)
+ * for more details). or by the landscape natural orientation of the device.
+ *
+ *
+ * The system could ignore [android.app.Activity.setRequestedOrientation]
+ * call from an app if both of the following conditions are true:
+ * * Activity has requested orientation more than 2 times within 1-second timer
+ * * Activity is not letterboxed for fixed orientation
+ *
+ * Setting this property to `false` informs the system that the app must be
+ * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+ * into the treatment.
+ *
+ * Not setting this property at all, or setting this property to `true` has no effect.
+ *
+ * **Syntax:**
+ * ```
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
+ * android:value="false" />
+ * </application>
+ * ```
+ */
+ // TODO(b/274924641): Make property public
+ const val PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
+ "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
+
+ /**
+ * Application level
+ * [PackageManager][android.content.pm.PackageManager.Property] tag
+ * for an app to inform the system that the app should be opted-out from the compatibility
+ * override that changes the min aspect ratio.
+ *
+ * When this compat override is enabled the min aspect ratio given in the app's manifest can
+ * be overridden by the device manufacturer using their discretion to improve display
+ * compatibility unless the app's manifest value is higher. This treatment will also apply if
+ * no min aspect ratio value is provided in the manifest. These treatments can apply only in
+ * specific cases (e.g. device is in portrait) or each time the app is displayed on screen.
+ *
+ * Setting this property to `false` informs the system that the app must be
+ * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+ * into the treatment.
+ *
+ * Not setting this property at all, or setting this property to `true` has no effect.
+ *
+ * **Syntax:**
+ * ```
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"
+ * android:value="false" />
+ * </application>
+ * ```
+ */
+ const val PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE =
+ "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"
+
+ /**
+ * Application level
+ * [PackageManager][android.content.pm.PackageManager.Property] tag
+ * for an app to inform the system that the app should be opted-out from the
+ * compatibility overrides that change the resizability of the app.
+ *
+ * When these compat overrides are enabled they force the packages they are applied to to be
+ * resizable / unresizable. If the app is forced to be resizable this won't change whether
+ * the app can be put into multi-windowing mode, but allow the app to resize without going into
+ * size-compat mode when the window container resizes, such as display size change or screen
+ * rotation.
+ *
+ * Setting this property to `false` informs the system that the app must be
+ * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+ * into the treatment.
+ *
+ * Not setting this property at all, or setting this property to `true` has no effect.
+ *
+ * **Syntax:**
+ * ```
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
+ * android:value="false" />
+ * </application>
+ * ```
+ */
+ const val PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES =
+ "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt b/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
index de0971a..07fcfd5 100644
--- a/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -17,27 +17,36 @@
package androidx.window.area
import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
+import android.os.Binder
import java.util.concurrent.Executor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
/**
- * Empty Implementation for devices that do not
- * support the [WindowAreaController] functionality
+ * Empty Implementation for devices that do not support the [WindowAreaController] functionality
*/
-@ExperimentalWindowApi
internal class EmptyWindowAreaControllerImpl : WindowAreaController {
- override fun rearDisplayStatus(): Flow<WindowAreaStatus> {
- return flowOf(WindowAreaStatus.UNSUPPORTED)
- }
- override fun rearDisplayMode(
+ override val windowAreaInfos: Flow<List<WindowAreaInfo>>
+ get() = flowOf(listOf())
+
+ override fun transferActivityToWindowArea(
+ token: Binder,
activity: Activity,
executor: Executor,
windowAreaSessionCallback: WindowAreaSessionCallback
) {
- // TODO(b/269144982): Investigate not throwing an exception
- throw UnsupportedOperationException("Rear Display mode cannot be enabled currently")
+ windowAreaSessionCallback.onSessionEnded(
+ IllegalStateException("There are no WindowAreas"))
+ }
+
+ override fun presentContentOnWindowArea(
+ token: Binder,
+ activity: Activity,
+ executor: Executor,
+ windowAreaPresentationSessionCallback: WindowAreaPresentationSessionCallback
+ ) {
+ windowAreaPresentationSessionCallback.onSessionEnded(
+ IllegalStateException("There are no WindowAreas"))
}
}
diff --git a/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
new file mode 100644
index 0000000..4c06141
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import android.content.Context
+import android.view.View
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation
+import androidx.window.extensions.area.WindowAreaComponent
+
+internal class RearDisplayPresentationSessionPresenterImpl(
+ private val windowAreaComponent: WindowAreaComponent,
+ private val presentation: ExtensionWindowAreaPresentation
+) : WindowAreaSessionPresenter {
+
+ override val context: Context = presentation.presentationContext
+
+ override fun setContentView(view: View) {
+ presentation.setPresentationView(view)
+ }
+
+ override fun close() {
+ windowAreaComponent.endRearDisplayPresentationSession()
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt b/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
index ae7d3ca..5a4a9a3 100644
--- a/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -16,10 +16,8 @@
package androidx.window.area
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.extensions.area.WindowAreaComponent
-@ExperimentalWindowApi
internal class RearDisplaySessionImpl(
private val windowAreaComponent: WindowAreaComponent
) : WindowAreaSession {
diff --git a/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt b/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt
new file mode 100644
index 0000000..8eb9696
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.window.area
+
+import androidx.window.SafeWindowExtensionsProvider
+import androidx.window.area.reflectionguard.WindowAreaComponentValidator.isExtensionWindowAreaPresentationValid
+import androidx.window.area.reflectionguard.WindowAreaComponentValidator.isExtensionWindowAreaStatusValid
+import androidx.window.area.reflectionguard.WindowAreaComponentValidator.isWindowAreaComponentValid
+import androidx.window.core.ExtensionsUtil
+import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.reflection.ReflectionUtils.doesReturn
+import androidx.window.reflection.ReflectionUtils.isPublic
+import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.reflection.WindowExtensionsConstants
+
+/**
+ * Reflection Guard for [WindowAreaComponent].
+ * This will go through the [WindowAreaComponent]'s method by reflection and
+ * check each method's name and signature to see if the interface is what we required.
+ */
+internal class SafeWindowAreaComponentProvider(private val loader: ClassLoader) {
+
+ private val windowExtensions = SafeWindowExtensionsProvider(loader).windowExtensions
+
+ val windowAreaComponent: WindowAreaComponent?
+ get() {
+ return try {
+ if (
+ windowExtensions != null &&
+ isWindowAreaProviderValid(windowExtensions) &&
+ isWindowAreaComponentValid(
+ windowAreaComponentClass, ExtensionsUtil.safeVendorApiLevel
+ ) &&
+ isExtensionWindowAreaStatusValid(
+ extensionWindowAreaStatusClass, ExtensionsUtil.safeVendorApiLevel
+ ) &&
+ isValidExtensionWindowPresentation()
+ ) windowExtensions.windowAreaComponent else null
+ } catch (e: Exception) {
+ null
+ }
+ }
+
+ private fun isWindowAreaProviderValid(windowExtensions: Any): Boolean {
+ return validateReflection(
+ "WindowExtensions#getWindowAreaComponent is not valid"
+ ) {
+ val getWindowAreaComponentMethod =
+ windowExtensions::class.java.getMethod("getWindowAreaComponent")
+ getWindowAreaComponentMethod.isPublic &&
+ getWindowAreaComponentMethod.doesReturn(windowAreaComponentClass)
+ }
+ }
+
+ private fun isValidExtensionWindowPresentation(): Boolean {
+ // Not required for API Level 2 or below
+ return ExtensionsUtil.safeVendorApiLevel <= 2 ||
+ isExtensionWindowAreaPresentationValid(
+ extensionWindowAreaPresentationClass, ExtensionsUtil.safeVendorApiLevel
+ )
+ }
+
+ private val windowAreaComponentClass: Class<*>
+ get() {
+ return loader.loadClass(WindowExtensionsConstants.WINDOW_AREA_COMPONENT_CLASS)
+ }
+
+ private val extensionWindowAreaStatusClass: Class<*>
+ get() {
+ return loader.loadClass(WindowExtensionsConstants.EXTENSION_WINDOW_AREA_STATUS_CLASS)
+ }
+
+ private val extensionWindowAreaPresentationClass: Class<*>
+ get() {
+ return loader.loadClass(
+ WindowExtensionsConstants.EXTENSION_WINDOW_AREA_PRESENTATION_CLASS
+ )
+ }
+}
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt b/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
index 65154449..24e7152 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
@@ -16,21 +16,31 @@
package androidx.window.area
-import androidx.window.core.ExperimentalWindowApi
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_ACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_AVAILABLE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_UNAVAILABLE
+import androidx.window.extensions.area.WindowAreaComponent.STATUS_UNSUPPORTED
/**
* Adapter object to assist in translating values received from [WindowAreaComponent]
* to developer friendly values in [WindowAreaController]
*/
-@ExperimentalWindowApi
internal object WindowAreaAdapter {
- internal fun translate(status: @WindowAreaComponent.WindowAreaStatus Int): WindowAreaStatus {
+ internal fun translate(
+ status: @WindowAreaComponent.WindowAreaStatus Int
+ ): WindowAreaCapability.Status {
return when (status) {
- WindowAreaComponent.STATUS_AVAILABLE -> WindowAreaStatus.AVAILABLE
- WindowAreaComponent.STATUS_UNAVAILABLE -> WindowAreaStatus.UNAVAILABLE
- else -> WindowAreaStatus.UNSUPPORTED
+ STATUS_UNSUPPORTED -> WINDOW_AREA_STATUS_UNSUPPORTED
+ STATUS_UNAVAILABLE -> WINDOW_AREA_STATUS_UNAVAILABLE
+ STATUS_AVAILABLE -> WINDOW_AREA_STATUS_AVAILABLE
+ STATUS_ACTIVE -> WINDOW_AREA_STATUS_ACTIVE
+ else -> WINDOW_AREA_STATUS_UNSUPPORTED
}
}
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt b/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt
new file mode 100644
index 0000000..3346bf2
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import android.app.Activity
+
+/**
+ * Represents a capability for a [WindowAreaInfo].
+ */
+class WindowAreaCapability internal constructor(val operation: Operation, val status: Status) {
+ override fun toString(): String {
+ return "Operation: $operation: Status: $status"
+ }
+
+ /**
+ * Represents the status of availability for a specific [WindowAreaCapability]
+ */
+ class Status private constructor(private val description: String) {
+ override fun toString(): String {
+ return description
+ }
+
+ companion object {
+ /**
+ * Status indicating that the WindowArea feature is not a supported feature on the
+ * device.
+ */
+ @JvmField
+ val WINDOW_AREA_STATUS_UNSUPPORTED = Status("UNSUPPORTED")
+
+ /**
+ * Status indicating that the WindowArea feature is currently not available to be
+ * enabled. This could be because a different feature is active, or the current device
+ * configuration doesn't allow it.
+ */
+ @JvmField
+ val WINDOW_AREA_STATUS_UNAVAILABLE = Status("UNAVAILABLE")
+
+ /**
+ * Status indicating that the WindowArea feature is available to be enabled.
+ */
+ @JvmField
+ val WINDOW_AREA_STATUS_AVAILABLE = Status("AVAILABLE")
+
+ /**
+ * Status indicating that the WindowArea feature is currently active.
+ */
+ @JvmField
+ val WINDOW_AREA_STATUS_ACTIVE = Status("ACTIVE")
+ }
+ }
+
+ /**
+ * Represents an operation that a [WindowAreaInfo] may support.
+ */
+ class Operation private constructor(private val description: String) {
+ override fun toString(): String {
+ return description
+ }
+
+ companion object {
+
+ /**
+ * Operation that transfers an [Activity] into a [WindowAreaInfo]
+ */
+ @JvmField
+ val OPERATION_TRANSFER_ACTIVITY_TO_AREA = Operation("TRANSFER")
+
+ /**
+ * Operation that presents additional content into a [WindowAreaInfo]
+ */
+ @JvmField
+ val OPERATION_PRESENT_ON_AREA = Operation("PRESENT")
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is WindowAreaCapability &&
+ operation == other.operation &&
+ status == other.status
+ }
+
+ override fun hashCode(): Int {
+ var result = operation.hashCode()
+ result = 31 * result + status.hashCode()
+ return result
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
index cc6f9eb..e02ab52 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -17,55 +17,113 @@
package androidx.window.area
import android.app.Activity
+import android.os.Binder
import android.os.Build
import android.util.Log
import androidx.annotation.RestrictTo
+import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
+import androidx.window.area.utils.DeviceUtils
import androidx.window.core.BuildConfig
-import androidx.window.core.ExperimentalWindowApi
+import androidx.window.core.ExtensionsUtil
import androidx.window.core.VerificationMode
-import androidx.window.extensions.WindowExtensionsProvider
-import androidx.window.extensions.area.WindowAreaComponent
import java.util.concurrent.Executor
import kotlinx.coroutines.flow.Flow
/**
- * An interface to provide information about available window areas on the device and an option
- * to use the rear display area of a foldable device, exclusively or concurrently with the internal
- * display.
- *
- * @hide
+ * An interface to provide the information and behavior around moving windows between
+ * displays or display areas on a device.
*
*/
-@ExperimentalWindowApi
interface WindowAreaController {
/**
- * Provides information about the current state of the window area of the rear display on the
- * device, if or when it is available. Rear Display mode can be invoked if the current status is
- * [WindowAreaStatus.AVAILABLE].
+ * [Flow] of the list of current [WindowAreaInfo]s that are currently available to be interacted
+ * with.
*/
- fun rearDisplayStatus(): Flow<WindowAreaStatus>
+ val windowAreaInfos: Flow<List<WindowAreaInfo>>
/**
- * Starts Rear Display Mode and moves the provided activity to the rear side of the device in
- * order to face the same direction as the primary device camera(s). When a rear display
- * mode is started, the system will turn on the rear display of the device to show the content
- * there, and can disable the internal display. The provided [Activity] is likely to get a
- * configuration change or being relaunched due to the difference in the internal and rear
- * display sizes on the device.
- * <p>Only the top visible application can request and use this mode. The system can dismiss the
- * mode if the user changes the device state.
- * <p>This method can only be called if the feature is supported on the device and is reported
- * as available in the current state through [rearDisplayStatus], otherwise it will
- * throw an [Exception].
+ * Starts a transfer session where the calling [Activity] is moved to the window area identified
+ * by the [token]. Updates on the session are provided through the [WindowAreaSessionCallback].
+ * Attempting to start a transfer session when the [WindowAreaInfo] does not return
+ * [WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE] will result in
+ * [WindowAreaSessionCallback.onSessionEnded] containing an [IllegalStateException]
+ *
+ * Only the top visible application can request to start a transfer session.
+ *
+ * The calling [Activity] will likely go through a configuration change since the window area
+ * it will be transferred to is usually different from the current area the [Activity] is in.
+ * The callback is retained during the lifetime of the session. If an [Activity] is captured in
+ * the callback and it does not handle the configuration change then it will be leaked. Consider
+ * using an [androidx.lifecycle.ViewModel] since that is meant to outlive the [Activity]
+ * lifecycle. If the [Activity] does override configuration changes, it is safe to have the
+ * [Activity] handle the WindowAreaSessionCallback. This guarantees that the calling [Activity]
+ * will continue to receive [WindowAreaSessionCallback.onSessionEnded] and keep a handle to the
+ * [WindowAreaSession] provided through [WindowAreaSessionCallback.onSessionStarted].
+ *
+ * The [windowAreaSessionCallback] provided will receive a call to
+ * [WindowAreaSessionCallback.onSessionStarted] after the [Activity] has been transferred to the
+ * window area. The transfer session will stay active until the session provided through
+ * [WindowAreaSessionCallback.onSessionStarted] is closed. Depending on the
+ * [WindowAreaInfo.Type] there may be other triggers that end the session, such as if a device
+ * state change makes the window area unavailable. One example of this is if the [Activity] is
+ * currently transferred to the [TYPE_REAR_FACING] window area of a foldable device, the session
+ * will be ended when the device is closed. When this occurs,
+ * [WindowAreaSessionCallback.onSessionEnded] is called.
+ *
+ * @param token [Binder] token identifying the window area to be transferred to.
+ * @param activity Base Activity making the call to [transferActivityToWindowArea].
+ * @param executor Executor used to provide updates to [windowAreaSessionCallback].
+ * @param windowAreaSessionCallback to be notified when the rear display session is started and
+ * ended.
+ *
+ * @see windowAreaInfos
*/
- fun rearDisplayMode(
+ fun transferActivityToWindowArea(
+ token: Binder,
activity: Activity,
executor: Executor,
+ // TODO(272064992) investigate how to make this safer from leaks
windowAreaSessionCallback: WindowAreaSessionCallback
)
+ /**
+ * Starts a presentation session on the [WindowAreaInfo] identified by the [token] and sends
+ * updates through the [WindowAreaPresentationSessionCallback].
+ *
+ * If a presentation session is attempted to be started without it being available,
+ * [WindowAreaPresentationSessionCallback.onSessionEnded] will be called immediately with an
+ * [IllegalStateException].
+ *
+ * Only the top visible application can request to start a presentation session.
+ *
+ * The presentation session will stay active until the presentation provided through
+ * [WindowAreaPresentationSessionCallback.onSessionStarted] is closed. The [WindowAreaInfo.Type]
+ * may provide different triggers to close the session such as if the calling application
+ * is no longer in the foreground, or there is a device state change that makes the window area
+ * unavailable to be presented on. One example scenario is if a [TYPE_REAR_FACING] window area
+ * is being presented to on a foldable device that is open and has 2 screens. If the device is
+ * closed and the internal display is turned off, the session would be ended and
+ * [WindowAreaPresentationSessionCallback.onSessionEnded] is called to notify that the session
+ * has been ended. The session may end prematurely if the device gets to a critical thermal
+ * level, or if power saver mode is enabled.
+ *
+ * @param token [Binder] token to identify which [WindowAreaInfo] is to be presented on
+ * @param activity An [Activity] that will present content on the Rear Display.
+ * @param executor Executor used to provide updates to [windowAreaPresentationSessionCallback].
+ * @param windowAreaPresentationSessionCallback to be notified of updates to the lifecycle of
+ * the currently enabled rear display presentation.
+ * @see windowAreaInfos
+ */
+ fun presentContentOnWindowArea(
+ token: Binder,
+ activity: Activity,
+ executor: Executor,
+ windowAreaPresentationSessionCallback: WindowAreaPresentationSessionCallback
+ )
+
public companion object {
+
private val TAG = WindowAreaController::class.simpleName
private var decorator: WindowAreaControllerDecorator = EmptyDecorator
@@ -76,24 +134,31 @@
@JvmName("getOrCreate")
@JvmStatic
fun getOrCreate(): WindowAreaController {
- var windowAreaComponentExtensions: WindowAreaComponent?
- try {
- // TODO(b/267972002): Introduce reflection guard for WindowAreaComponent
- windowAreaComponentExtensions = WindowExtensionsProvider
- .getWindowExtensions()
- .windowAreaComponent
+ val windowAreaComponentExtensions = try {
+ this::class.java.classLoader?.let {
+ SafeWindowAreaComponentProvider(it).windowAreaComponent
+ }
} catch (t: Throwable) {
- if (BuildConfig.verificationMode == VerificationMode.STRICT) {
+ if (BuildConfig.verificationMode == VerificationMode.LOG) {
Log.d(TAG, "Failed to load WindowExtensions")
}
- windowAreaComponentExtensions = null
+ null
}
+ val deviceSupported = Build.VERSION.SDK_INT > Build.VERSION_CODES.Q &&
+ windowAreaComponentExtensions != null &&
+ (ExtensionsUtil.safeVendorApiLevel >= 3 || DeviceUtils.hasDeviceMetrics(
+ Build.MANUFACTURER,
+ Build.MODEL
+ ))
+
val controller =
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
- windowAreaComponentExtensions == null) {
- EmptyWindowAreaControllerImpl()
+ if (deviceSupported) {
+ WindowAreaControllerImpl(
+ windowAreaComponentExtensions!!,
+ ExtensionsUtil.safeVendorApiLevel
+ )
} else {
- WindowAreaControllerImpl(windowAreaComponentExtensions)
+ EmptyWindowAreaControllerImpl()
}
return decorator.decorate(controller)
}
@@ -116,7 +181,6 @@
* Decorator that allows us to provide different functionality
* in our window-testing artifact.
*/
-@ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface WindowAreaControllerDecorator {
/**
@@ -126,7 +190,6 @@
public fun decorate(controller: WindowAreaController): WindowAreaController
}
-@ExperimentalWindowApi
private object EmptyDecorator : WindowAreaControllerDecorator {
override fun decorate(controller: WindowAreaController): WindowAreaController {
return controller
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
index af9a398..b31d974 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -17,21 +17,29 @@
package androidx.window.area
import android.app.Activity
+import android.os.Binder
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
+import androidx.window.area.utils.DeviceUtils
import androidx.window.core.BuildConfig
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.core.VerificationMode
+import androidx.window.extensions.area.ExtensionWindowAreaStatus
import androidx.window.extensions.area.WindowAreaComponent
import androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_CONTENT_VISIBLE
import androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_INACTIVE
+import androidx.window.extensions.area.WindowAreaComponent.WindowAreaSessionState
import androidx.window.extensions.core.util.function.Consumer
+import androidx.window.layout.WindowMetrics
+import androidx.window.layout.WindowMetricsCalculator
import java.util.concurrent.Executor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
/**
* Implementation of WindowAreaController for devices
@@ -42,52 +50,243 @@
* [Build.VERSION_CODES.S] as that's the min level of support for
* this functionality.
*/
-@ExperimentalWindowApi
-@RequiresApi(Build.VERSION_CODES.N)
+@RequiresApi(Build.VERSION_CODES.Q)
internal class WindowAreaControllerImpl(
- private val windowAreaComponent: WindowAreaComponent
+ private val windowAreaComponent: WindowAreaComponent,
+ private val vendorApiLevel: Int
) : WindowAreaController {
- private var currentStatus: WindowAreaStatus? = null
+ private lateinit var rearDisplaySessionConsumer: Consumer<Int>
+ private var currentRearDisplayModeStatus: WindowAreaCapability.Status =
+ WINDOW_AREA_STATUS_UNSUPPORTED
+ private var currentRearDisplayPresentationStatus: WindowAreaCapability.Status =
+ WINDOW_AREA_STATUS_UNSUPPORTED
- override fun rearDisplayStatus(): Flow<WindowAreaStatus> {
- return callbackFlow {
- val listener = Consumer<@WindowAreaComponent.WindowAreaStatus Int> { status ->
- currentStatus = WindowAreaAdapter.translate(status)
- channel.trySend(currentStatus ?: WindowAreaStatus.UNSUPPORTED)
+ private val currentWindowAreaInfoMap = HashMap<String, WindowAreaInfo>()
+
+ override val windowAreaInfos: Flow<List<WindowAreaInfo>>
+ get() {
+ return callbackFlow {
+ val rearDisplayListener = Consumer<Int> { status ->
+ updateRearDisplayAvailability(status)
+ channel.trySend(currentWindowAreaInfoMap.values.toList())
+ }
+ val rearDisplayPresentationListener =
+ Consumer<ExtensionWindowAreaStatus> { extensionWindowAreaStatus ->
+ updateRearDisplayPresentationAvailability(extensionWindowAreaStatus)
+ channel.trySend(currentWindowAreaInfoMap.values.toList())
+ }
+
+ windowAreaComponent.addRearDisplayStatusListener(rearDisplayListener)
+ if (vendorApiLevel > 2) {
+ windowAreaComponent.addRearDisplayPresentationStatusListener(
+ rearDisplayPresentationListener
+ )
+ }
+
+ awaitClose {
+ windowAreaComponent.removeRearDisplayStatusListener(rearDisplayListener)
+ if (vendorApiLevel > 2) {
+ windowAreaComponent.removeRearDisplayPresentationStatusListener(
+ rearDisplayPresentationListener
+ )
+ }
+ }
}
- windowAreaComponent.addRearDisplayStatusListener(listener)
- awaitClose {
- windowAreaComponent.removeRearDisplayStatusListener(listener)
+ }
+
+ private fun updateRearDisplayAvailability(
+ status: @WindowAreaComponent.WindowAreaStatus Int
+ ) {
+ val windowMetrics = if (vendorApiLevel >= 3) {
+ WindowMetricsCalculator.fromDisplayMetrics(
+ displayMetrics = windowAreaComponent.rearDisplayMetrics
+ )
+ } else {
+ val displayMetrics = DeviceUtils.getRearDisplayMetrics(Build.MANUFACTURER, Build.MODEL)
+ if (displayMetrics != null) {
+ WindowMetricsCalculator.fromDisplayMetrics(
+ displayMetrics = displayMetrics
+ )
+ } else {
+ throw IllegalArgumentException(
+ "DeviceUtils rear display metrics entry should not be null"
+ )
}
- }.distinctUntilChanged()
+ }
+
+ currentRearDisplayModeStatus = WindowAreaAdapter.translate(status)
+ updateRearDisplayWindowArea(
+ WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA,
+ currentRearDisplayModeStatus,
+ windowMetrics
+ )
}
- override fun rearDisplayMode(
+ private fun updateRearDisplayPresentationAvailability(
+ extensionWindowAreaStatus: ExtensionWindowAreaStatus
+ ) {
+ currentRearDisplayPresentationStatus =
+ WindowAreaAdapter.translate(extensionWindowAreaStatus.windowAreaStatus)
+ val windowMetrics = WindowMetricsCalculator.fromDisplayMetrics(
+ displayMetrics = extensionWindowAreaStatus.windowAreaDisplayMetrics
+ )
+
+ updateRearDisplayWindowArea(
+ WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA,
+ currentRearDisplayPresentationStatus,
+ windowMetrics,
+ )
+ }
+
+ /**
+ * Updates the [WindowAreaInfo] object with the [REAR_DISPLAY_BINDER_DESCRIPTOR] binder token
+ * with the updated [status] corresponding to the [operation] and with the updated [metrics]
+ * received from the device for this window area.
+ *
+ * @param operation Operation that we are updating the status of.
+ * @param status New status for the operation provided on this window area.
+ * @param metrics Updated [WindowMetrics] for this window area.
+ */
+ private fun updateRearDisplayWindowArea(
+ operation: WindowAreaCapability.Operation,
+ status: WindowAreaCapability.Status,
+ metrics: WindowMetrics,
+ ) {
+ var rearDisplayAreaInfo: WindowAreaInfo? =
+ currentWindowAreaInfoMap[REAR_DISPLAY_BINDER_DESCRIPTOR]
+ if (status == WINDOW_AREA_STATUS_UNSUPPORTED) {
+ rearDisplayAreaInfo?.let { info ->
+ if (shouldRemoveWindowAreaInfo(info)) {
+ currentWindowAreaInfoMap.remove(REAR_DISPLAY_BINDER_DESCRIPTOR)
+ } else {
+ val capability = WindowAreaCapability(operation, status)
+ info.capabilityMap[operation] = capability
+ }
+ }
+ } else {
+ if (rearDisplayAreaInfo == null) {
+ rearDisplayAreaInfo = WindowAreaInfo(
+ metrics = metrics,
+ type = WindowAreaInfo.Type.TYPE_REAR_FACING,
+ // TODO(b/273807238): Update extensions to send the binder token and type
+ token = Binder(REAR_DISPLAY_BINDER_DESCRIPTOR),
+ windowAreaComponent = windowAreaComponent
+ )
+ }
+ val capability = WindowAreaCapability(operation, status)
+ rearDisplayAreaInfo.capabilityMap[operation] = capability
+ rearDisplayAreaInfo.metrics = metrics
+ currentWindowAreaInfoMap[REAR_DISPLAY_BINDER_DESCRIPTOR] = rearDisplayAreaInfo
+ }
+ }
+
+ /**
+ * Determines if a [WindowAreaInfo] should be removed from [windowAreaInfos] if all
+ * [WindowAreaCapability] are currently [WINDOW_AREA_STATUS_UNSUPPORTED]
+ */
+ private fun shouldRemoveWindowAreaInfo(windowAreaInfo: WindowAreaInfo): Boolean {
+ for (capability: WindowAreaCapability in windowAreaInfo.capabilityMap.values) {
+ if (capability.status != WINDOW_AREA_STATUS_UNSUPPORTED) {
+ return false
+ }
+ }
+ return true
+ }
+
+ override fun transferActivityToWindowArea(
+ token: Binder,
+ activity: Activity,
+ executor: Executor,
+ windowAreaSessionCallback: WindowAreaSessionCallback
+ ) {
+ if (token.interfaceDescriptor == REAR_DISPLAY_BINDER_DESCRIPTOR) {
+ startRearDisplayMode(activity, executor, windowAreaSessionCallback)
+ }
+ }
+
+ override fun presentContentOnWindowArea(
+ token: Binder,
+ activity: Activity,
+ executor: Executor,
+ windowAreaPresentationSessionCallback: WindowAreaPresentationSessionCallback
+ ) {
+ if (token.interfaceDescriptor == REAR_DISPLAY_BINDER_DESCRIPTOR) {
+ startRearDisplayPresentationMode(
+ activity,
+ executor,
+ windowAreaPresentationSessionCallback
+ )
+ }
+ }
+
+ private fun startRearDisplayMode(
activity: Activity,
executor: Executor,
windowAreaSessionCallback: WindowAreaSessionCallback
) {
- // If we already have a status value that is not [WindowAreaStatus.AVAILABLE]
- // we should throw an exception quick to indicate they tried to enable
- // RearDisplay mode when it was not available.
- if (currentStatus != null && currentStatus != WindowAreaStatus.AVAILABLE) {
- throw UnsupportedOperationException("Rear Display mode cannot be enabled currently")
+ // If the capability is currently active, provide an error pointing the developer on how to
+ // get access to the current session
+ if (currentRearDisplayModeStatus == WINDOW_AREA_STATUS_ACTIVE) {
+ windowAreaSessionCallback.onSessionEnded(
+ IllegalStateException(
+ "The WindowArea feature is currently active, WindowAreaInfo#getActiveSession" +
+ "can be used to get an instance of the current active session"
+ )
+ )
+ return
}
- val rearDisplaySessionConsumer =
+
+ // If we already have an availability value that is not
+ // [Availability.WINDOW_AREA_CAPABILITY_AVAILABLE] we should end the session and pass an
+ // exception to indicate they tried to enable rear display mode when it was not available.
+ if (currentRearDisplayModeStatus != WINDOW_AREA_STATUS_AVAILABLE) {
+ windowAreaSessionCallback.onSessionEnded(
+ IllegalStateException(
+ "The WindowArea feature is currently not available to be entered"
+ )
+ )
+ return
+ }
+
+ rearDisplaySessionConsumer =
RearDisplaySessionConsumer(executor, windowAreaSessionCallback, windowAreaComponent)
windowAreaComponent.startRearDisplaySession(activity, rearDisplaySessionConsumer)
}
+ private fun startRearDisplayPresentationMode(
+ activity: Activity,
+ executor: Executor,
+ windowAreaPresentationSessionCallback: WindowAreaPresentationSessionCallback
+ ) {
+ if (currentRearDisplayPresentationStatus != WINDOW_AREA_STATUS_AVAILABLE) {
+ windowAreaPresentationSessionCallback.onSessionEnded(
+ IllegalStateException(
+ "The WindowArea feature is currently not available to be entered"
+ )
+ )
+ return
+ }
+
+ windowAreaComponent.startRearDisplayPresentationSession(
+ activity,
+ RearDisplayPresentationSessionConsumer(
+ executor,
+ windowAreaPresentationSessionCallback,
+ windowAreaComponent
+ )
+ )
+ }
+
internal class RearDisplaySessionConsumer(
private val executor: Executor,
private val appCallback: WindowAreaSessionCallback,
private val extensionsComponent: WindowAreaComponent
- ) : Consumer<@WindowAreaComponent.WindowAreaSessionState Int> {
+ ) : Consumer<Int> {
private var session: WindowAreaSession? = null
- override fun accept(t: @WindowAreaComponent.WindowAreaSessionState Int) {
+ override fun accept(t: Int) {
when (t) {
SESSION_STATE_ACTIVE -> onSessionStarted()
SESSION_STATE_INACTIVE -> onSessionFinished()
@@ -107,11 +306,58 @@
private fun onSessionFinished() {
session = null
- executor.execute { appCallback.onSessionEnded() }
+ executor.execute { appCallback.onSessionEnded(null) }
+ }
+ }
+
+ internal class RearDisplayPresentationSessionConsumer(
+ private val executor: Executor,
+ private val windowAreaPresentationSessionCallback: WindowAreaPresentationSessionCallback,
+ private val windowAreaComponent: WindowAreaComponent
+ ) : Consumer<@WindowAreaSessionState Int> {
+
+ private var lastReportedSessionStatus: @WindowAreaSessionState Int = SESSION_STATE_INACTIVE
+ override fun accept(t: @WindowAreaSessionState Int) {
+ val previousStatus: @WindowAreaSessionState Int = lastReportedSessionStatus
+ lastReportedSessionStatus = t
+
+ executor.execute {
+ when (t) {
+ SESSION_STATE_ACTIVE -> {
+ // If the last status was visible, then ACTIVE infers the content is no
+ // longer visible.
+ if (previousStatus == SESSION_STATE_CONTENT_VISIBLE) {
+ windowAreaPresentationSessionCallback.onContainerVisibilityChanged(
+ false /* isVisible */
+ )
+ } else {
+ // Presentation should never be null if the session is active
+ windowAreaPresentationSessionCallback.onSessionStarted(
+ RearDisplayPresentationSessionPresenterImpl(
+ windowAreaComponent,
+ windowAreaComponent.rearDisplayPresentation!!
+ )
+ )
+ }
+ }
+
+ SESSION_STATE_CONTENT_VISIBLE ->
+ windowAreaPresentationSessionCallback.onContainerVisibilityChanged(true)
+
+ SESSION_STATE_INACTIVE ->
+ windowAreaPresentationSessionCallback.onSessionEnded(null)
+
+ else -> {
+ Log.e(TAG, "Invalid session state value received: $t")
+ }
+ }
+ }
}
}
internal companion object {
private val TAG = WindowAreaControllerImpl::class.simpleName
+
+ private const val REAR_DISPLAY_BINDER_DESCRIPTOR = "WINDOW_AREA_REAR_DISPLAY"
}
}
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
new file mode 100644
index 0000000..e38bdaa
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import android.os.Binder
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_PRESENT_ON_AREA
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
+import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
+import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.layout.WindowMetrics
+
+/**
+ * The current state of a window area. The [WindowAreaInfo] can represent a part of or an entire
+ * display in the system. These values can be used to modify the UI to show/hide controls and
+ * determine when features can be enabled.
+ */
+class WindowAreaInfo internal constructor(
+
+ /**
+ * The [WindowMetrics] that represent the size of the area. Used to determine if the behavior
+ * desired fits the size of the window area available.
+ */
+ var metrics: WindowMetrics,
+
+ /**
+ * The [Type] of this window area
+ */
+ val type: Type,
+
+ /**
+ * [Binder] token to identify the specific WindowArea
+ */
+ val token: Binder,
+
+ private val windowAreaComponent: WindowAreaComponent
+) {
+
+ internal val capabilityMap = HashMap<WindowAreaCapability.Operation, WindowAreaCapability>()
+
+ /**
+ * Returns the [WindowAreaCapability] corresponding to the [operation] provided. If this
+ * [WindowAreaCapability] does not exist for this [WindowAreaInfo], null is returned.
+ */
+ fun getCapability(operation: WindowAreaCapability.Operation): WindowAreaCapability? {
+ return capabilityMap[operation]
+ }
+
+ /**
+ * Returns the current active [WindowAreaSession] is one is currently active for the provided
+ * [operation]
+ *
+ * @throws IllegalStateException if there is no active session for the provided [operation]
+ */
+ fun getActiveSession(operation: WindowAreaCapability.Operation): WindowAreaSession? {
+ if (getCapability(operation)?.status != WINDOW_AREA_STATUS_ACTIVE) {
+ throw IllegalStateException("No session is currently active")
+ }
+
+ if (type == Type.TYPE_REAR_FACING) {
+ // TODO(b/273807246) We should cache instead of always creating a new session
+ return createRearFacingSession(operation)
+ }
+ return null
+ }
+
+ private fun createRearFacingSession(
+ operation: WindowAreaCapability.Operation
+ ): WindowAreaSession {
+ return when (operation) {
+ OPERATION_TRANSFER_ACTIVITY_TO_AREA -> RearDisplaySessionImpl(windowAreaComponent)
+ OPERATION_PRESENT_ON_AREA ->
+ RearDisplayPresentationSessionPresenterImpl(
+ windowAreaComponent,
+ windowAreaComponent.rearDisplayPresentation!!
+ )
+ else -> {
+ throw IllegalArgumentException("Invalid operation provided")
+ }
+ }
+ }
+
+ /**
+ * Represents a type of [WindowAreaInfo]
+ */
+ class Type private constructor(private val description: String) {
+ override fun toString(): String {
+ return description
+ }
+
+ companion object {
+ /**
+ * Type of window area that is facing the same direction as the rear camera(s) on the
+ * device.
+ */
+ @JvmField
+ val TYPE_REAR_FACING = Type("REAR FACING")
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is WindowAreaInfo &&
+ metrics == other.metrics &&
+ type == other.type &&
+ capabilityMap.entries == other.capabilityMap.entries
+ }
+
+ override fun hashCode(): Int {
+ var result = metrics.hashCode()
+ result = 31 * result + type.hashCode()
+ result = 31 * result + capabilityMap.entries.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "WindowAreaInfo{ Metrics: $metrics, type: $type, " +
+ "Capabilities: ${capabilityMap.entries} }"
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt b/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt
new file mode 100644
index 0000000..2d4b8ce
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import android.content.Context
+import android.view.View
+
+/**
+ * A callback to notify about the lifecycle of a window area presentation session.
+ *
+ * @see WindowAreaController.presentContentOnWindowArea
+ */
+interface WindowAreaPresentationSessionCallback {
+
+ /**
+ * Notifies about a start of a presentation session. Provides a reference to
+ * [WindowAreaSessionPresenter] to allow an application to customize a presentation when the
+ * session starts. The [Context] provided from the [WindowAreaSessionPresenter] should be used
+ * to inflate or make any UI decisions around the presentation [View] that should be shown in
+ * that area.
+ */
+ fun onSessionStarted(session: WindowAreaSessionPresenter)
+
+ /**
+ * Notifies about an end of a presentation session. The presentation and any app-provided
+ * content in the window area is removed.
+ *
+ * @param t [Throwable] to provide information on if the session was ended due to an error.
+ * This will only occur if a session is attempted to be enabled when it is not available, but
+ * can be expanded to alert for more errors in the future.
+ */
+ fun onSessionEnded(t: Throwable?)
+
+ /**
+ * Notifies about changes in visibility of a container that can hold the app content to show
+ * in the window area. Notification of the container being visible is guaranteed to occur after
+ * [onSessionStarted] has been called. The container being no longer visible is guaranteed to
+ * occur before [onSessionEnded].
+ *
+ * If content was never presented, then this method will never be called.
+ */
+ fun onContainerVisibilityChanged(isVisible: Boolean)
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
index e3e4bff..41ca43ea 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -16,17 +16,15 @@
package androidx.window.area
-import androidx.window.core.ExperimentalWindowApi
-
/**
- * Session interface to represent a long-standing
- * WindowArea mode or feature that provides a handle
- * to close the session.
+ * Session interface to represent an active window area feature.
*
- * @hide
- *
+ * @see WindowAreaSessionCallback.onSessionStarted
*/
-@ExperimentalWindowApi
interface WindowAreaSession {
+
+ /**
+ * Closes the active session, no-op if the session is not currently active.
+ */
fun close()
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
index 7527d53..b76e175 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 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.
@@ -16,20 +16,25 @@
package androidx.window.area
-import androidx.window.core.ExperimentalWindowApi
-
/**
- * Callback to update the client on the WindowArea Session being
+ * Callback to update the client on the WindowArea Session being
* started and ended.
* TODO(b/207720511) Move to window-java module when Kotlin API Finalized
- *
- * @hide
- *
*/
-@ExperimentalWindowApi
interface WindowAreaSessionCallback {
+ /**
+ * Notifies about a start of a session. Provides a reference to the current [WindowAreaSession]
+ * the application the ability to close the session through [WindowAreaSession.close].
+ */
fun onSessionStarted(session: WindowAreaSession)
- fun onSessionEnded()
+ /**
+ * Notifies about an end of a [WindowAreaSession].
+ *
+ * @param t [Throwable] to provide information on if the session was ended due to an error.
+ * This will only occur if a session is attempted to be enabled when it is not available, but
+ * can be expanded to alert for more errors in the future.
+ */
+ fun onSessionEnded(t: Throwable?)
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
new file mode 100644
index 0000000..bc8bfc8
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area
+
+import android.content.Context
+import android.view.View
+
+/**
+ * A container that allows getting access to and showing content on a window area. The container is
+ * provided from [WindowAreaPresentationSessionCallback] when a requested session becomes active.
+ * The presentation can be automatically dismissed by the system when the user leaves the primary
+ * application window, or can be closed by calling [WindowAreaSessionPresenter.close].
+ * @see WindowAreaController.presentContentOnWindowArea
+ */
+interface WindowAreaSessionPresenter : WindowAreaSession {
+ /**
+ * Returns the [Context] associated with the window area.
+ */
+ val context: Context
+
+ /**
+ * Sets a [View] to show on a window area. After setting the view the system can turn on the
+ * corresponding display and start showing content.
+ */
+ fun setContentView(view: View)
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaStatus.kt b/window/window/src/main/java/androidx/window/area/WindowAreaStatus.kt
deleted file mode 100644
index 732da7d..0000000
--- a/window/window/src/main/java/androidx/window/area/WindowAreaStatus.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.area
-
-import androidx.window.core.ExperimentalWindowApi
-
-/**
- * Represents a window area status.
- *
- * @hide
- *
- */
-@ExperimentalWindowApi
-class WindowAreaStatus private constructor(private val mDescription: String) {
-
- override fun toString(): String {
- return mDescription
- }
-
- companion object {
- /**
- * Status representing that the WindowArea feature is not a supported
- * feature on the device.
- */
- @JvmField
- val UNSUPPORTED = WindowAreaStatus("UNSUPPORTED")
-
- /**
- * Status representing that the WindowArea feature is currently not available
- * to be enabled. This could be due to another process has enabled it, or that the
- * current device configuration doesn't allow it.
- */
- @JvmField
- val UNAVAILABLE = WindowAreaStatus("UNAVAILABLE")
-
- /**
- * Status representing that the WindowArea feature is available to be enabled.
- */
- @JvmField
- val AVAILABLE = WindowAreaStatus("AVAILABLE")
- }
-}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaPresentationRequirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaPresentationRequirements.java
new file mode 100644
index 0000000..9153250
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaPresentationRequirements.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation;
+
+/**
+ * API requirements for [ExtensionWindowAreaPresentation]
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public interface ExtensionWindowAreaPresentationRequirements {
+ /** @see ExtensionWindowAreaPresentation#getPresentationContext */
+ @NonNull
+ Context getPresentationContext();
+
+ /** @see ExtensionWindowAreaPresentation#setPresentationView */
+ void setPresentationView(@NonNull View view);
+}
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaStatusRequirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaStatusRequirements.java
new file mode 100644
index 0000000..14ba999
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/ExtensionWindowAreaStatusRequirements.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard;
+
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.area.ExtensionWindowAreaStatus;
+
+/**
+ * API requirements for [ExtensionWindowAreaStatus]
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public interface ExtensionWindowAreaStatusRequirements {
+ /** @see ExtensionWindowAreaStatus#getWindowAreaStatus */
+ int getWindowAreaStatus();
+
+ /** @see ExtensionWindowAreaStatus#getWindowAreaDisplayMetrics */
+ @NonNull
+ DisplayMetrics getWindowAreaDisplayMetrics();
+}
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java
new file mode 100644
index 0000000..0ab78c0
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.area.WindowAreaComponent;
+import androidx.window.extensions.core.util.function.Consumer;
+
+/**
+ * This file defines the Vendor API Level 2 Requirements for WindowAreaComponent. This is used
+ * in the client library to perform reflection guard to ensure that the OEM extension implementation
+ * is complete.
+ *
+ * @see WindowAreaComponent
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public interface WindowAreaComponentApi2Requirements {
+
+ /** @see WindowAreaComponent#addRearDisplayStatusListener */
+ void addRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+ /** @see WindowAreaComponent#removeRearDisplayStatusListener */
+ void removeRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+ /** @see WindowAreaComponent#startRearDisplaySession */
+ void startRearDisplaySession(@NonNull Activity activity,
+ @NonNull Consumer<Integer> consumer);
+
+ /** @see WindowAreaComponent#endRearDisplaySession */
+ void endRearDisplaySession();
+}
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java
new file mode 100644
index 0000000..56c54aa
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard;
+
+import android.app.Activity;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation;
+import androidx.window.extensions.area.ExtensionWindowAreaStatus;
+import androidx.window.extensions.area.WindowAreaComponent;
+import androidx.window.extensions.core.util.function.Consumer;
+
+
+/**
+ * This file defines the Vendor API Level 3 Requirements for WindowAreaComponent. This is used
+ * in the client library to perform reflection guard to ensure that the OEM extension implementation
+ * is complete.
+ *
+ * @see WindowAreaComponent
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public interface WindowAreaComponentApi3Requirements extends WindowAreaComponentApi2Requirements {
+
+ /** @see WindowAreaComponent#addRearDisplayPresentationStatusListener */
+ void addRearDisplayPresentationStatusListener(
+ @NonNull Consumer<ExtensionWindowAreaStatus> consumer);
+
+ /** @see WindowAreaComponent#removeRearDisplayPresentationStatusListener */
+ void removeRearDisplayPresentationStatusListener(
+ @NonNull Consumer<ExtensionWindowAreaStatus> consumer);
+
+ /** @see WindowAreaComponent#startRearDisplayPresentationSession */
+ void startRearDisplayPresentationSession(@NonNull Activity activity,
+ @NonNull Consumer<Integer> consumer);
+
+ /** @see WindowAreaComponent#endRearDisplayPresentationSession */
+ void endRearDisplayPresentationSession();
+
+ /** @see WindowAreaComponent#getRearDisplayPresentation */
+ @Nullable
+ ExtensionWindowAreaPresentation getRearDisplayPresentation();
+
+ /** @see WindowAreaComponent#getRearDisplayMetrics() */
+ @NonNull
+ DisplayMetrics getRearDisplayMetrics();
+}
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt
new file mode 100644
index 0000000..d48d2ab
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.reflectionguard
+
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation
+import androidx.window.extensions.area.WindowAreaComponent
+import androidx.window.reflection.ReflectionUtils.validateImplementation
+
+/**
+ * Utility class to validate [WindowAreaComponent] implementation.
+ */
+internal object WindowAreaComponentValidator {
+
+ internal fun isWindowAreaComponentValid(windowAreaComponent: Class<*>, apiLevel: Int): Boolean {
+ return when {
+ apiLevel <= 1 -> false
+ apiLevel == 2 -> validateImplementation(
+ windowAreaComponent, WindowAreaComponentApi2Requirements::class.java
+ )
+ else -> validateImplementation(
+ windowAreaComponent, WindowAreaComponentApi3Requirements::class.java
+ )
+ }
+ }
+
+ internal fun isExtensionWindowAreaStatusValid(
+ extensionWindowAreaStatus: Class<*>,
+ apiLevel: Int
+ ): Boolean {
+ return when {
+ apiLevel <= 1 -> false
+ else -> validateImplementation(
+ extensionWindowAreaStatus, ExtensionWindowAreaStatusRequirements::class.java
+ )
+ }
+ }
+
+ internal fun isExtensionWindowAreaPresentationValid(
+ extensionWindowAreaPresentation: Class<*>,
+ apiLevel: Int
+ ): Boolean {
+ return when {
+ apiLevel <= 2 -> false
+ else -> validateImplementation(
+ extensionWindowAreaPresentation, ExtensionWindowAreaPresentation::class.java
+ )
+ }
+ }
+}
diff --git a/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt b/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt
new file mode 100644
index 0000000..009d892
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.utils
+
+import android.os.Build
+import android.util.DisplayMetrics
+import androidx.annotation.RequiresApi
+
+/**
+ * Data class holding metrics about a specific device.
+ */
+@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+internal class DeviceMetrics(
+ val manufacturer: String,
+ val model: String,
+ val rearDisplayMetrics: DisplayMetrics
+) {
+ override fun equals(other: Any?): Boolean {
+ return other is DeviceMetrics &&
+ manufacturer == other.manufacturer &&
+ model == other.model &&
+ rearDisplayMetrics.equals(other.rearDisplayMetrics)
+ }
+
+ override fun hashCode(): Int {
+ var result = manufacturer.hashCode()
+ result = 31 * result + model.hashCode()
+ result = 31 * result + rearDisplayMetrics.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "DeviceMetrics{ Manufacturer: $manufacturer, model: $model, " +
+ "Rear display metrics: $rearDisplayMetrics }"
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt b/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt
new file mode 100644
index 0000000..44bb3fa
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.utils
+
+import android.os.Build
+import android.util.DisplayMetrics
+import androidx.annotation.RequiresApi
+import java.util.Locale
+
+/**
+ * Utility object to provide information about specific devices that may not be available
+ * through the extensions API at a certain vendor API level
+ */
+@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+internal object DeviceUtils {
+
+ private val deviceList = listOf(DeviceMetrics("google", "pixel fold",
+ DisplayMetrics().apply {
+ widthPixels = 1080
+ heightPixels = 2092
+ density = 2.625f
+ densityDpi = 420 }
+ ))
+
+ internal fun hasDeviceMetrics(manufacturer: String, model: String): Boolean {
+ return deviceList.any {
+ it.manufacturer == manufacturer.lowercase(Locale.US) &&
+ it.model == model.lowercase(Locale.US)
+ }
+ }
+
+ internal fun getRearDisplayMetrics(manufacturer: String, model: String): DisplayMetrics? {
+ return deviceList.firstOrNull {
+ it.manufacturer == manufacturer.lowercase(Locale.US) &&
+ it.model == model.lowercase(Locale.US)
+ }?.rearDisplayMetrics
+ }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
index 219cb1f..8adffae 100644
--- a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
@@ -17,7 +17,10 @@
package androidx.window.embedding
import android.app.Activity
+import android.app.ActivityOptions
import android.content.Context
+import android.os.IBinder
+import androidx.window.core.ExperimentalWindowApi
/** The controller that allows checking the current [Activity] embedding status. */
class ActivityEmbeddingController internal constructor(private val backend: EmbeddingBackend) {
@@ -31,6 +34,68 @@
fun isActivityEmbedded(activity: Activity): Boolean =
backend.isActivityEmbedded(activity)
+ /**
+ * Returns the [ActivityStack] that this [activity] is part of when it is being organized in the
+ * embedding container and associated with a [SplitInfo]. Returns `null` if there is no such
+ * [ActivityStack].
+ *
+ * @param activity The [Activity] to check.
+ * @return the [ActivityStack] that this [activity] is part of, or `null` if there is no such
+ * [ActivityStack].
+ */
+ @ExperimentalWindowApi
+ fun getActivityStack(activity: Activity): ActivityStack? =
+ backend.getActivityStack(activity)
+
+ /**
+ * Sets the launching [ActivityStack] to the given [android.app.ActivityOptions].
+ *
+ * @param options The [android.app.ActivityOptions] to be updated.
+ * @param token The token of the [ActivityStack] to be set.
+ */
+ internal fun setLaunchingActivityStack(
+ options: ActivityOptions,
+ token: IBinder
+ ): ActivityOptions {
+ return backend.setLaunchingActivityStack(options, token)
+ }
+
+ /**
+ * Finishes a set of [activityStacks][ActivityStack] from the lowest to the highest z-order
+ * regardless of the order of [ActivityStack] set.
+ *
+ * If the remaining [ActivityStack] from a split participates in other splits with other
+ * `activityStacks`, they might be showing instead. For example, if activityStack A splits with
+ * activityStack B and C, and activityStack C covers activityStack B, finishing activityStack C
+ * might make the split of activityStack A and B show.
+ *
+ * If all associated `activityStacks` of a [ActivityStack] are finished, the [ActivityStack]
+ * will be expanded to fill the parent task container. This is useful to expand the primary
+ * container as the sample linked below shows.
+ *
+ * **Note** that it's caller's responsibility to check whether this API is supported by calling
+ * [isFinishingActivityStacksSupported]. If not, an alternative approach to finishing all
+ * containers above a particular activity can be to launch it again with flag
+ * [android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP].
+ *
+ * @param activityStacks The set of [ActivityStack] to be finished.
+ * @throws UnsupportedOperationException if this device doesn't support this API and
+ * [isFinishingActivityStacksSupported] returns `false`.
+ * @sample androidx.window.samples.embedding.expandPrimaryContainer
+ */
+ @ExperimentalWindowApi
+ fun finishActivityStacks(activityStacks: Set<ActivityStack>) =
+ backend.finishActivityStacks(activityStacks)
+
+ /**
+ * Checks whether [finishActivityStacks] is supported.
+ *
+ * @return `true` if [finishActivityStacks] is supported on the device, `false` otherwise.
+ */
+ @ExperimentalWindowApi
+ fun isFinishingActivityStacksSupported(): Boolean =
+ backend.isFinishActivityStacksSupported()
+
companion object {
/**
* Obtains an instance of [ActivityEmbeddingController].
@@ -43,4 +108,4 @@
return ActivityEmbeddingController(backend)
}
}
-}
\ No newline at end of file
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt
new file mode 100644
index 0000000..190ffa3
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("ActivityEmbeddingOptions")
+
+package androidx.window.embedding
+
+import android.app.Activity
+import android.app.ActivityOptions
+import android.content.Context
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.core.ExtensionsUtil
+import androidx.window.extensions.WindowExtensions
+
+/**
+ * Sets the launching [ActivityStack] to the given [android.app.ActivityOptions].
+ *
+ * If the device doesn't support setting launching, [UnsupportedOperationException] will be thrown.
+ * @see isSetLaunchingActivityStackSupported
+ *
+ * @param context The [android.content.Context] that is going to be used for launching
+ * activity with this [android.app.ActivityOptions], which is usually be the [android.app.Activity]
+ * of the app that hosts the task.
+ * @param activityStack The target [ActivityStack] for launching.
+ * @throws UnsupportedOperationException if this device doesn't support this API.
+ */
+@ExperimentalWindowApi
+fun ActivityOptions.setLaunchingActivityStack(
+ context: Context,
+ activityStack: ActivityStack
+): ActivityOptions = let {
+ if (!isSetLaunchingActivityStackSupported()) {
+ throw UnsupportedOperationException("#setLaunchingActivityStack is not " +
+ "supported on the device.")
+ } else {
+ ActivityEmbeddingController.getInstance(context)
+ .setLaunchingActivityStack(this, activityStack.token)
+ }
+}
+
+/**
+ * Sets the launching [ActivityStack] to the [android.app.ActivityOptions] by the
+ * given [activity]. That is, the [ActivityStack] of the given [activity] is the
+ * [ActivityStack] used for launching.
+ *
+ * If the device doesn't support setting launching or no available [ActivityStack]
+ * can be found from the given [activity], [UnsupportedOperationException] will be thrown.
+ * @see isSetLaunchingActivityStackSupported
+ *
+ * @param activity The existing [android.app.Activity] on the target [ActivityStack].
+ * @throws UnsupportedOperationException if this device doesn't support this API or no
+ * available [ActivityStack] can be found.
+ */
+@ExperimentalWindowApi
+fun ActivityOptions.setLaunchingActivityStack(activity: Activity): ActivityOptions {
+ val activityStack =
+ ActivityEmbeddingController.getInstance(activity).getActivityStack(activity)
+ return if (activityStack != null) {
+ setLaunchingActivityStack(activity, activityStack)
+ } else {
+ throw UnsupportedOperationException("No available ActivityStack found. " +
+ "The given activity may not be embedded.")
+ }
+}
+
+/**
+ * Return `true` if the [setLaunchingActivityStack] APIs is supported and can be used
+ * to set the launching [ActivityStack]. Otherwise, return `false`.
+ */
+@ExperimentalWindowApi
+fun ActivityOptions.isSetLaunchingActivityStackSupported(): Boolean {
+ return ExtensionsUtil.safeVendorApiLevel >= WindowExtensions.VENDOR_API_LEVEL_3
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt b/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
index 59b0839..ba9f8a9 100644
--- a/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
@@ -16,6 +16,7 @@
package androidx.window.embedding
import android.app.Activity
+import android.os.IBinder
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
@@ -40,7 +41,11 @@
* process(es), [activitiesInProcess] will return an empty list, but this method will return
* `false`.
*/
- val isEmpty: Boolean
+ val isEmpty: Boolean,
+ /**
+ * A token uniquely identifying this `ActivityStack`.
+ */
+ internal val token: IBinder,
) {
/**
@@ -56,6 +61,7 @@
if (activitiesInProcess != other.activitiesInProcess) return false
if (isEmpty != other.isEmpty) return false
+ if (token != other.token) return false
return true
}
@@ -63,6 +69,7 @@
override fun hashCode(): Int {
var result = activitiesInProcess.hashCode()
result = 31 * result + isEmpty.hashCode()
+ result = 31 * result + token.hashCode()
return result
}
@@ -70,5 +77,6 @@
"ActivityStack{" +
"activitiesInProcess=$activitiesInProcess" +
", isEmpty=$isEmpty" +
+ ", token=$token" +
"}"
}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
index 3340a8b..ce56396 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
@@ -20,10 +20,10 @@
import android.app.Activity
import android.content.Context
import android.content.Intent
+import android.os.Binder
import android.util.LayoutDirection
import android.util.Pair as AndroidPair
import android.view.WindowMetrics
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.core.ExtensionsUtil
import androidx.window.core.PredicateAdapter
import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
@@ -56,7 +56,6 @@
import androidx.window.extensions.embedding.SplitPlaceholderRule.Builder as SplitPlaceholderRuleBuilder
import androidx.window.layout.WindowMetricsCalculator
import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter
-import kotlin.Pair
/**
* Adapter class that translates data classes between Extension and Jetpack interfaces.
@@ -82,13 +81,16 @@
SplitInfo(
ActivityStack(
primaryActivityStack.activities,
- primaryActivityStack.isEmpty
+ primaryActivityStack.isEmpty,
+ primaryActivityStack.token,
),
ActivityStack(
secondaryActivityStack.activities,
- secondaryActivityStack.isEmpty
+ secondaryActivityStack.isEmpty,
+ secondaryActivityStack.token,
),
- translate(splitInfo.splitAttributes)
+ translate(splitInfo.splitAttributes),
+ splitInfo.token,
)
}
}
@@ -117,14 +119,12 @@
)
.build()
- @OptIn(ExperimentalWindowApi::class)
fun translateSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
): Function<OEMSplitAttributesCalculatorParams, OEMSplitAttributes> = Function { oemParams ->
translateSplitAttributes(calculator.invoke(translate(oemParams)))
}
- @OptIn(ExperimentalWindowApi::class)
@SuppressLint("NewApi")
fun translate(
params: OEMSplitAttributesCalculatorParams
@@ -322,18 +322,21 @@
val primaryActivityStack = splitInfo.primaryActivityStack
val primaryFragment = ActivityStack(
primaryActivityStack.activities,
- primaryActivityStack.isEmpty
+ primaryActivityStack.isEmpty,
+ INVALID_ACTIVITY_STACK_TOKEN,
)
val secondaryActivityStack = splitInfo.secondaryActivityStack
val secondaryFragment = ActivityStack(
secondaryActivityStack.activities,
- secondaryActivityStack.isEmpty
+ secondaryActivityStack.isEmpty,
+ INVALID_ACTIVITY_STACK_TOKEN,
)
return SplitInfo(
primaryFragment,
secondaryFragment,
- translate(splitInfo.splitAttributes)
+ translate(splitInfo.splitAttributes),
+ INVALID_SPLIT_INFO_TOKEN,
)
}
}
@@ -501,12 +504,28 @@
ActivityStack(
splitInfo.primaryActivityStack.activities,
splitInfo.primaryActivityStack.isEmpty,
+ INVALID_ACTIVITY_STACK_TOKEN,
),
ActivityStack(
splitInfo.secondaryActivityStack.activities,
splitInfo.secondaryActivityStack.isEmpty,
+ INVALID_ACTIVITY_STACK_TOKEN,
),
getSplitAttributesCompat(splitInfo),
+ INVALID_SPLIT_INFO_TOKEN,
)
}
+
+ internal companion object {
+ /**
+ * The default token of [SplitInfo], which provides compatibility for device prior to
+ * [WindowExtensions.VENDOR_API_LEVEL_3]
+ */
+ val INVALID_SPLIT_INFO_TOKEN = Binder()
+ /**
+ * The default token of [ActivityStack], which provides compatibility for device prior to
+ * [WindowExtensions.VENDOR_API_LEVEL_3]
+ */
+ val INVALID_ACTIVITY_STACK_TOKEN = Binder()
+ }
}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
index 02b8a67..c8825c0 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
@@ -17,10 +17,11 @@
package androidx.window.embedding
import android.app.Activity
+import android.app.ActivityOptions
import android.content.Context
+import android.os.IBinder
import androidx.annotation.RestrictTo
import androidx.core.util.Consumer
-import androidx.window.core.ExperimentalWindowApi
import java.util.concurrent.Executor
/**
@@ -50,7 +51,6 @@
fun isActivityEmbedded(activity: Activity): Boolean
- @ExperimentalWindowApi
fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
)
@@ -59,6 +59,20 @@
fun isSplitAttributesCalculatorSupported(): Boolean
+ fun getActivityStack(activity: Activity): ActivityStack?
+
+ fun setLaunchingActivityStack(options: ActivityOptions, token: IBinder): ActivityOptions
+
+ fun finishActivityStacks(activityStacks: Set<ActivityStack>)
+
+ fun isFinishActivityStacksSupported(): Boolean
+
+ fun invalidateTopVisibleSplitAttributes()
+
+ fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes)
+
+ fun areSplitAttributesUpdatesSupported(): Boolean
+
companion object {
private var decorator: (EmbeddingBackend) -> EmbeddingBackend =
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
index d54b21a..a3559bb 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
@@ -17,16 +17,18 @@
package androidx.window.embedding
import android.app.Activity
+import android.app.ActivityOptions
import android.content.Context
+import android.os.IBinder
import android.util.Log
import androidx.window.core.BuildConfig
import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.core.ExtensionsUtil
import androidx.window.core.VerificationMode
import androidx.window.embedding.EmbeddingInterfaceCompat.EmbeddingCallbackInterface
import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_AVAILABLE
import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_2
+import androidx.window.extensions.WindowExtensions.VENDOR_API_LEVEL_3
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.core.util.function.Consumer
import androidx.window.extensions.embedding.ActivityEmbeddingComponent
@@ -90,7 +92,6 @@
return embeddingExtension.isActivityEmbedded(activity)
}
- @ExperimentalWindowApi
override fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
) {
@@ -114,6 +115,50 @@
override fun isSplitAttributesCalculatorSupported(): Boolean =
ExtensionsUtil.safeVendorApiLevel >= VENDOR_API_LEVEL_2
+ override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+ if (!isFinishActivityStacksSupported()) {
+ throw UnsupportedOperationException("#finishActivityStacks is not " +
+ "supported on the device.")
+ }
+ val stackTokens = activityStacks.mapTo(mutableSetOf()) { it.token }
+ embeddingExtension.finishActivityStacks(stackTokens)
+ }
+
+ override fun isFinishActivityStacksSupported(): Boolean =
+ ExtensionsUtil.safeVendorApiLevel >= VENDOR_API_LEVEL_3
+
+ override fun invalidateTopVisibleSplitAttributes() {
+ if (!areSplitAttributesUpdatesSupported()) {
+ throw UnsupportedOperationException("#invalidateTopVisibleSplitAttributes is not " +
+ "supported on the device.")
+ }
+ embeddingExtension.invalidateTopVisibleSplitAttributes()
+ }
+
+ override fun updateSplitAttributes(
+ splitInfo: SplitInfo,
+ splitAttributes: SplitAttributes
+ ) {
+ if (!areSplitAttributesUpdatesSupported()) {
+ throw UnsupportedOperationException("#updateSplitAttributes is not supported on the " +
+ "device.")
+ }
+ embeddingExtension.updateSplitAttributes(
+ splitInfo.token,
+ adapter.translateSplitAttributes(splitAttributes)
+ )
+ }
+
+ override fun areSplitAttributesUpdatesSupported(): Boolean =
+ ExtensionsUtil.safeVendorApiLevel >= VENDOR_API_LEVEL_3
+
+ override fun setLaunchingActivityStack(
+ options: ActivityOptions,
+ token: IBinder
+ ): ActivityOptions {
+ return embeddingExtension.setLaunchingActivityStack(options, token)
+ }
+
companion object {
const val DEBUG = true
private const val TAG = "EmbeddingCompat"
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
index c9830a5..26d8846 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
@@ -17,7 +17,8 @@
package androidx.window.embedding
import android.app.Activity
-import androidx.window.core.ExperimentalWindowApi
+import android.app.ActivityOptions
+import android.os.IBinder
import androidx.window.extensions.embedding.ActivityEmbeddingComponent
/**
@@ -36,7 +37,6 @@
fun isActivityEmbedded(activity: Activity): Boolean
- @ExperimentalWindowApi
fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
)
@@ -44,4 +44,16 @@
fun clearSplitAttributesCalculator()
fun isSplitAttributesCalculatorSupported(): Boolean
+
+ fun setLaunchingActivityStack(options: ActivityOptions, token: IBinder): ActivityOptions
+
+ fun finishActivityStacks(activityStacks: Set<ActivityStack>)
+
+ fun isFinishActivityStacksSupported(): Boolean
+
+ fun invalidateTopVisibleSplitAttributes()
+
+ fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes)
+
+ fun areSplitAttributesUpdatesSupported(): Boolean
}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt b/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
index d8371b7..9a931db 100644
--- a/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
@@ -17,9 +17,11 @@
package androidx.window.embedding
import android.app.Activity
+import android.app.ActivityOptions
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
+import android.os.IBinder
import android.util.Log
import androidx.annotation.DoNotInline
import androidx.annotation.GuardedBy
@@ -30,7 +32,6 @@
import androidx.window.WindowProperties
import androidx.window.core.BuildConfig
import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.core.ExtensionsUtil
import androidx.window.core.PredicateAdapter
import androidx.window.core.VerificationMode
@@ -335,7 +336,6 @@
return embeddingExtension?.isActivityEmbedded(activity) ?: false
}
- @ExperimentalWindowApi
override fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
) {
@@ -353,6 +353,49 @@
override fun isSplitAttributesCalculatorSupported(): Boolean =
embeddingExtension?.isSplitAttributesCalculatorSupported() ?: false
+ override fun getActivityStack(activity: Activity): ActivityStack? {
+ globalLock.withLock {
+ val lastInfo: List<SplitInfo> = splitInfoEmbeddingCallback.lastInfo ?: return null
+ for (info in lastInfo) {
+ if (activity !in info) {
+ continue
+ }
+ if (activity in info.primaryActivityStack) {
+ return info.primaryActivityStack
+ }
+ if (activity in info.secondaryActivityStack) {
+ return info.secondaryActivityStack
+ }
+ }
+ return null
+ }
+ }
+
+ override fun setLaunchingActivityStack(
+ options: ActivityOptions,
+ token: IBinder
+ ): ActivityOptions = embeddingExtension?.setLaunchingActivityStack(options, token) ?: options
+
+ override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+ embeddingExtension?.finishActivityStacks(activityStacks)
+ }
+
+ override fun isFinishActivityStacksSupported(): Boolean =
+ embeddingExtension?.isFinishActivityStacksSupported() ?: false
+
+ override fun invalidateTopVisibleSplitAttributes() {
+ embeddingExtension?.invalidateTopVisibleSplitAttributes()
+ }
+
+ override fun updateSplitAttributes(
+ splitInfo: SplitInfo,
+ splitAttributes: SplitAttributes
+ ) {
+ embeddingExtension?.updateSplitAttributes(splitInfo, splitAttributes)
+ }
+
+ override fun areSplitAttributesUpdatesSupported(): Boolean =
+ embeddingExtension?.areSplitAttributesUpdatesSupported() ?: false
@RequiresApi(31)
private object Api31Impl {
@DoNotInline
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitAttributesCalculatorParams.kt b/window/window/src/main/java/androidx/window/embedding/SplitAttributesCalculatorParams.kt
index 8da62c6..40453be 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitAttributesCalculatorParams.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitAttributesCalculatorParams.kt
@@ -18,7 +18,6 @@
import android.content.res.Configuration
import androidx.annotation.RestrictTo
-import androidx.window.core.ExperimentalWindowApi
import androidx.window.layout.WindowLayoutInfo
import androidx.window.layout.WindowMetrics
@@ -27,7 +26,6 @@
* [SplitController.setSplitAttributesCalculator] and references the corresponding [SplitRule] by
* [splitRuleTag] if [SplitPairRule.tag] is specified.
*/
-@ExperimentalWindowApi
class SplitAttributesCalculatorParams @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor(
/** The parent container's [WindowMetrics] */
val parentWindowMetrics: WindowMetrics,
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitController.kt b/window/window/src/main/java/androidx/window/embedding/SplitController.kt
index 41f46b8..660b15b 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitController.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitController.kt
@@ -155,9 +155,8 @@
* example, a foldable device with multiple screens can choose to collapse
* splits when apps run on the device's small display, but enable splits
* when apps run on the device's large display. In cases like this,
- * [splitSupportStatus] always returns [SplitSupportStatus.SPLIT_AVAILABLE], and if the
- * split is collapsed, activities are launched on top, following the non-activity
- * embedding model.
+ * [splitSupportStatus] always returns [SplitSupportStatus.SPLIT_AVAILABLE], and if the split is
+ * collapsed, activities are launched on top, following the non-activity embedding model.
*
* Also the [androidx.window.WindowProperties.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED]
* must be enabled in AndroidManifest within <application> in order to get the correct
@@ -213,7 +212,6 @@
* @throws UnsupportedOperationException if [isSplitAttributesCalculatorSupported] reports
* `false`
*/
- @ExperimentalWindowApi
fun setSplitAttributesCalculator(
calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
) {
@@ -227,19 +225,85 @@
* @throws UnsupportedOperationException if [isSplitAttributesCalculatorSupported] reports
* `false`
*/
- @ExperimentalWindowApi
fun clearSplitAttributesCalculator() {
embeddingBackend.clearSplitAttributesCalculator()
}
/** Returns whether [setSplitAttributesCalculator] is supported or not. */
- @ExperimentalWindowApi
fun isSplitAttributesCalculatorSupported(): Boolean =
embeddingBackend.isSplitAttributesCalculatorSupported()
/**
+ * Triggers a [SplitAttributes] update callback for the current topmost and visible split layout
+ * if there is one. This method can be used when a change to the split presentation originates
+ * from an application state change. Changes that are driven by parent window changes or new
+ * activity starts invoke the callback provided in [setSplitAttributesCalculator] automatically
+ * without the need to call this function.
+ *
+ * The top [SplitInfo] is usually the last element of [SplitInfo] list which was received from
+ * the callback registered in [SplitController.addSplitListener].
+ *
+ * The call will be ignored if there is no visible split.
+ *
+ * @throws UnsupportedOperationException if the device doesn't support this API.
+ */
+ @ExperimentalWindowApi
+ fun invalidateTopVisibleSplitAttributes() =
+ embeddingBackend.invalidateTopVisibleSplitAttributes()
+
+ /**
+ * Checks whether [invalidateTopVisibleSplitAttributes] is supported on the device.
+ *
+ * Invoking these APIs if the feature is not supported would trigger an
+ * [UnsupportedOperationException].
+ * @return `true` if the runtime APIs to update [SplitAttributes] are supported and can be
+ * called safely, `false` otherwise.
+ */
+ @ExperimentalWindowApi
+ fun isInvalidatingTopVisibleSplitAttributesSupported(): Boolean =
+ embeddingBackend.areSplitAttributesUpdatesSupported()
+
+ /**
+ * Updates the [SplitAttributes] of a split pair. This is an alternative to using
+ * a split attributes calculator callback set in [setSplitAttributesCalculator], useful when
+ * apps only need to update the splits in a few cases proactively but rely on the default split
+ * attributes most of the time otherwise.
+ *
+ * The provided split attributes will be used instead of the associated
+ * [SplitRule.defaultSplitAttributes].
+ *
+ * **Note** that the split attributes may be updated if split attributes calculator callback is
+ * registered and invoked. If [setSplitAttributesCalculator] is used, the callback will still be
+ * applied to each [SplitInfo] when there's either:
+ * - A new Activity being launched.
+ * - A window or device state updates (e,g. due to screen rotation or folding state update).
+ *
+ * In most cases it is suggested to use [invalidateTopVisibleSplitAttributes] if
+ * [SplitAttributes] calculator callback is used.
+ *
+ * @param splitInfo the split pair to update
+ * @param splitAttributes the [SplitAttributes] to be applied
+ * @throws UnsupportedOperationException if this device doesn't support this API
+ */
+ @ExperimentalWindowApi
+ fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes) =
+ embeddingBackend.updateSplitAttributes(splitInfo, splitAttributes)
+
+ /**
+ * Checks whether [updateSplitAttributes] is supported on the device.
+ *
+ * Invoking these APIs if the feature is not supported would trigger an
+ * [UnsupportedOperationException].
+ * @return `true` if the runtime APIs to update [SplitAttributes] are supported and can be
+ * called safely, `false` otherwise.
+ */
+ @ExperimentalWindowApi
+ fun isUpdatingSplitAttributesSupported(): Boolean =
+ embeddingBackend.areSplitAttributesUpdatesSupported()
+
+ /**
* A class to determine if activity splits with Activity Embedding are currently available.
- * "Depending on the split property declaration, device software version or user preferences
+ * Depending on the split property declaration, device software version or user preferences
* the feature might not be available.
*/
class SplitSupportStatus private constructor(private val rawValue: Int) {
@@ -291,4 +355,4 @@
return SplitController(backend)
}
}
-}
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt b/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
index f366ca7..81adda5 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
@@ -17,6 +17,7 @@
package androidx.window.embedding
import android.app.Activity
+import android.os.IBinder
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
@@ -31,7 +32,11 @@
*/
val secondaryActivityStack: ActivityStack,
/** The [SplitAttributes] of this split pair. */
- val splitAttributes: SplitAttributes
+ val splitAttributes: SplitAttributes,
+ /**
+ * A token uniquely identifying this `SplitInfo`.
+ */
+ internal val token: IBinder,
) {
/**
* Whether the [primaryActivityStack] or the [secondaryActivityStack] in this [SplitInfo]
@@ -49,6 +54,7 @@
if (primaryActivityStack != other.primaryActivityStack) return false
if (secondaryActivityStack != other.secondaryActivityStack) return false
if (splitAttributes != other.splitAttributes) return false
+ if (token != other.token) return false
return true
}
@@ -57,6 +63,7 @@
var result = primaryActivityStack.hashCode()
result = 31 * result + secondaryActivityStack.hashCode()
result = 31 * result + splitAttributes.hashCode()
+ result = 31 * result + token.hashCode()
return result
}
@@ -66,6 +73,7 @@
append("primaryActivityStack=$primaryActivityStack, ")
append("secondaryActivityStack=$secondaryActivityStack, ")
append("splitAttributes=$splitAttributes, ")
+ append("token=$token")
append("}")
}
}
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
index c989e32..765876e 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
@@ -22,14 +22,13 @@
import android.view.WindowMetrics
import androidx.annotation.DoNotInline
import androidx.annotation.IntRange
-import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
-import androidx.core.os.BuildCompat
import androidx.core.util.Preconditions
import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_ALLOW
import androidx.window.embedding.EmbeddingAspectRatio.Companion.ratio
import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW
import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ADJACENT
import kotlin.math.min
@@ -231,15 +230,16 @@
* Verifies if the provided parent bounds satisfy the dimensions and aspect ratio requirements
* to apply the rule.
*/
- // TODO(b/265089843) remove after Build.VERSION_CODES.U released.
- @OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
internal fun checkParentMetrics(context: Context, parentMetrics: WindowMetrics): Boolean {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
return false
}
val bounds = Api30Impl.getBounds(parentMetrics)
- // TODO(b/265089843) replace with Build.VERSION.SDK_INT >= Build.VERSION_CODES.U
- val density = context.resources.displayMetrics.density
+ val density = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
+ context.resources.displayMetrics.density
+ } else {
+ Api34Impl.getDensity(parentMetrics, context)
+ }
return checkParentBounds(density, bounds)
}
@@ -288,6 +288,19 @@
}
}
+ @RequiresApi(34)
+ internal object Api34Impl {
+ @DoNotInline
+ fun getDensity(windowMetrics: WindowMetrics, context: Context): Float {
+ // TODO(b/265089843) remove the try catch after U is finalized.
+ return try {
+ windowMetrics.density
+ } catch (e: NoSuchMethodError) {
+ context.resources.displayMetrics.density
+ }
+ }
+ }
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SplitRule) return false
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
index 94dd455..132ff58 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
@@ -20,12 +20,14 @@
import android.content.Context
import android.inputmethodservice.InputMethodService
import android.os.Build
+import android.util.DisplayMetrics
import android.view.Display
import android.view.WindowMetrics as AndroidWindowMetrics
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.annotation.UiContext
import androidx.core.view.WindowInsetsCompat
+import androidx.window.core.Bounds
/**
* An interface to calculate the [WindowMetrics] for an [Activity] or a [UiContext].
@@ -153,6 +155,13 @@
windowMetrics.bounds,
WindowInsetsCompat.toWindowInsetsCompat(windowMetrics.windowInsets)
)
+
+ internal fun fromDisplayMetrics(displayMetrics: DisplayMetrics): WindowMetrics {
+ return WindowMetrics(
+ Bounds(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels),
+ WindowInsetsCompat.Builder().build()
+ )
+ }
}
}
diff --git a/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt b/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
index 326486e..ed8b7ee 100644
--- a/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
+++ b/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
@@ -80,4 +80,16 @@
internal fun Method.doesReturn(clazz: Class<*>): Boolean {
return returnType.equals(clazz)
}
-}
\ No newline at end of file
+
+ internal fun validateImplementation(
+ implementation: Class<*>,
+ requirements: Class<*>,
+ ): Boolean {
+ return requirements.methods.all {
+ validateReflection("${implementation.name}#${it.name} is not valid") {
+ val implementedMethod = implementation.getMethod(it.name, *it.parameterTypes)
+ implementedMethod.isPublic && implementedMethod.doesReturn(it.returnType)
+ }
+ }
+ }
+}
diff --git a/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt b/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
index 341e019..b86fdc1 100644
--- a/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
+++ b/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
@@ -53,6 +53,27 @@
"$WINDOW_EXTENSIONS_PACKAGE_NAME.layout.WindowLayoutComponent"
/**
+ * Constant name for class [androidx.window.extensions.area.WindowAreaComponent]
+ * used for reflection
+ */
+ internal const val WINDOW_AREA_COMPONENT_CLASS =
+ "$WINDOW_EXTENSIONS_PACKAGE_NAME.area.WindowAreaComponent"
+
+ /**
+ * Constant name for class [androidx.window.extensions.area.ExtensionWindowAreaStatus]
+ * used for reflection
+ */
+ internal const val EXTENSION_WINDOW_AREA_STATUS_CLASS =
+ "$WINDOW_EXTENSIONS_PACKAGE_NAME.area.ExtensionWindowAreaStatus"
+
+ /**
+ * Constant name for class [androidx.window.extensions.area.ExtensionWindowAreaPresentation]
+ * used for reflection
+ */
+ internal const val EXTENSION_WINDOW_AREA_PRESENTATION_CLASS =
+ "$WINDOW_EXTENSIONS_PACKAGE_NAME.area.ExtensionWindowAreaPresentation"
+
+ /**
* Constant name for class [androidx.window.extensions.embedding.ActivityEmbeddingComponent]
* used for reflection
*/
diff --git a/window/window/src/test/java/androidx/window/area/WindowAreaAdapterUnitTest.kt b/window/window/src/test/java/androidx/window/area/WindowAreaAdapterUnitTest.kt
deleted file mode 100644
index 89a9808..0000000
--- a/window/window/src/test/java/androidx/window/area/WindowAreaAdapterUnitTest.kt
+++ /dev/null
@@ -1,49 +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.window.area
-
-import androidx.window.core.ExperimentalWindowApi
-import androidx.window.extensions.area.WindowAreaComponent
-import org.junit.Test
-
-/**
- * Unit tests for [WindowAreaAdapter] that run on the JVM.
- */
-@OptIn(ExperimentalWindowApi::class)
-class WindowAreaAdapterUnitTest {
-
- @Test
- fun testWindowAreaStatusTranslateValueAvailable() {
- val expected = WindowAreaStatus.AVAILABLE
- val translateValue = WindowAreaAdapter.translate(WindowAreaComponent.STATUS_AVAILABLE)
- assert(expected == translateValue)
- }
-
- @Test
- fun testWindowAreaStatusTranslateValueUnavailable() {
- val expected = WindowAreaStatus.UNAVAILABLE
- val translateValue = WindowAreaAdapter.translate(WindowAreaComponent.STATUS_UNAVAILABLE)
- assert(expected == translateValue)
- }
-
- @Test
- fun testWindowAreaStatusTranslateValueUnsupported() {
- val expected = WindowAreaStatus.UNSUPPORTED
- val translateValue = WindowAreaAdapter.translate(WindowAreaComponent.STATUS_UNSUPPORTED)
- assert(expected == translateValue)
- }
-}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
index b13af0c..1f84586 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
@@ -17,7 +17,9 @@
package androidx.window.embedding
import android.app.Activity
+import android.os.Binder
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mockito.kotlin.mock
@@ -27,7 +29,7 @@
@Test
fun testContainsActivity() {
val activity = mock<Activity>()
- val stack = ActivityStack(listOf(activity), isEmpty = false)
+ val stack = ActivityStack(listOf(activity), isEmpty = false, Binder())
assertTrue(activity in stack)
}
@@ -35,10 +37,17 @@
@Test
fun testEqualsImpliesHashCode() {
val activity = mock<Activity>()
- val first = ActivityStack(listOf(activity), isEmpty = false)
- val second = ActivityStack(listOf(activity), isEmpty = false)
+ val token = Binder()
+ val first = ActivityStack(listOf(activity), isEmpty = false, token)
+ val second = ActivityStack(listOf(activity), isEmpty = false, token)
assertEquals(first, second)
assertEquals(first.hashCode(), second.hashCode())
+
+ val anotherToken = Binder()
+ val third = ActivityStack(emptyList(), isEmpty = true, anotherToken)
+
+ assertNotEquals(first, third)
+ assertNotEquals(first.hashCode(), third.hashCode())
}
}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
index 3ad6e58..e297829 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
@@ -45,9 +45,10 @@
@Test
fun test_splitInfoListComesFromBackend() = testScope.runTest {
val expected = listOf(SplitInfo(
- ActivityStack(emptyList(), true),
- ActivityStack(emptyList(), true),
- SplitAttributes()
+ ActivityStack(emptyList(), true, mock()),
+ ActivityStack(emptyList(), true, mock()),
+ SplitAttributes(),
+ mock()
))
doAnswer { invocationOnMock ->
@Suppress("UNCHECKED_CAST")
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
index 07c0855..780bcf9 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
@@ -17,6 +17,9 @@
package androidx.window.embedding
import android.app.Activity
+import android.os.Binder
+import android.os.IBinder
+import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_ACTIVITY_STACK_TOKEN
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -30,7 +33,8 @@
val firstStack = createTestActivityStack(listOf(activity))
val secondStack = createTestActivityStack(emptyList())
val attributes = SplitAttributes()
- val info = SplitInfo(firstStack, secondStack, attributes)
+ val token = Binder()
+ val info = SplitInfo(firstStack, secondStack, attributes, token)
assertTrue(info.contains(activity))
}
@@ -41,7 +45,8 @@
val firstStack = createTestActivityStack(emptyList())
val secondStack = createTestActivityStack(listOf(activity))
val attributes = SplitAttributes()
- val info = SplitInfo(firstStack, secondStack, attributes)
+ val token = Binder()
+ val info = SplitInfo(firstStack, secondStack, attributes, token)
assertTrue(info.contains(activity))
}
@@ -52,8 +57,9 @@
val firstStack = createTestActivityStack(emptyList())
val secondStack = createTestActivityStack(listOf(activity))
val attributes = SplitAttributes()
- val firstInfo = SplitInfo(firstStack, secondStack, attributes)
- val secondInfo = SplitInfo(firstStack, secondStack, attributes)
+ val token = Binder()
+ val firstInfo = SplitInfo(firstStack, secondStack, attributes, token)
+ val secondInfo = SplitInfo(firstStack, secondStack, attributes, token)
assertEquals(firstInfo, secondInfo)
assertEquals(firstInfo.hashCode(), secondInfo.hashCode())
@@ -62,5 +68,6 @@
private fun createTestActivityStack(
activitiesInProcess: List<Activity>,
isEmpty: Boolean = false,
- ): ActivityStack = ActivityStack(activitiesInProcess, isEmpty)
+ token: IBinder = INVALID_ACTIVITY_STACK_TOKEN,
+ ): ActivityStack = ActivityStack(activitiesInProcess, isEmpty, token)
}
\ No newline at end of file
diff --git a/window/window/src/test/resources/robolectric.properties b/window/window/src/test/resources/robolectric.properties
new file mode 100644
index 0000000..69fde47
--- /dev/null
+++ b/window/window/src/test/resources/robolectric.properties
@@ -0,0 +1,3 @@
+# robolectric properties
+# Temporary until we update Robolectric to support API level 34.
+sdk=33
diff --git a/work/work-datatransfer/build.gradle b/work/work-datatransfer/build.gradle
new file mode 100644
index 0000000..77f47f7
--- /dev/null
+++ b/work/work-datatransfer/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+android {
+ namespace "androidx.work.datatransfer"
+}
+
+dependencies {
+ api(project(":work:work-runtime-ktx"))
+ api(libs.kotlinStdlib)
+}
+
+androidx {
+ name = "WorkManager Data Transfer"
+ publish = Publish.SNAPSHOT_ONLY
+ inceptionYear = "2023"
+ description = "Android WorkManager Data Transfer library"
+}