Merge "Stop trying to draw ripples while unattached" into androidx-main
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
index de994f6..8bcdb8c 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
@@ -23,6 +23,7 @@
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.internal.util.TestUtil;
+import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraXConfig;
import androidx.camera.core.ImageAnalysis;
@@ -88,7 +89,8 @@
mCamera = CameraUtil.createCameraAndAttachUseCase(context, cameraSelector, imageAnalysis);
mCameraControl = TestUtil.getCamera2CameraControlImpl(mCamera.getCameraControl());
mTorchControl = mCameraControl.getTorchControl();
- mIsTorchStrengthSupported = mCamera.getCameraInfo().getMaxTorchStrengthLevel() > 1;
+ mIsTorchStrengthSupported = mCamera.getCameraInfo().getMaxTorchStrengthLevel()
+ != CameraInfo.TORCH_STRENGTH_LEVEL_UNSUPPORTED;
}
@After
@@ -118,7 +120,7 @@
@Test(timeout = 5000L)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- public void setTorchStrengthLevel_futureCompleteWhenTorchIsOnLevel()
+ public void setTorchStrengthLevel_futureCompleteWhenTorchIsOn()
throws ExecutionException, InterruptedException {
assumeTrue(mIsTorchStrengthSupported);
@@ -132,7 +134,7 @@
@Test(timeout = 5000L)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- public void setTorchStrengthLevel_futureCompleteWhenTorchIsOffLevel()
+ public void setTorchStrengthLevel_futureCompleteWhenTorchIsOff()
throws ExecutionException, InterruptedException {
assumeTrue(mIsTorchStrengthSupported);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index 8a2a439..d6321f2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -516,6 +516,10 @@
return Futures.immediateFailedFuture(
new OperationCanceledException("Camera is not active."));
}
+ if (!mCameraCharacteristics.isTorchStrengthLevelSupported()) {
+ return Futures.immediateFailedFuture(new UnsupportedOperationException(
+ "The device doesn't support configuring torch strength level."));
+ }
if (torchStrengthLevel < 1
|| torchStrengthLevel > mCameraCharacteristics.getMaxTorchStrengthLevel()) {
return Futures.immediateFailedFuture(new IllegalArgumentException(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
index aeb7058..4804b521 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
@@ -719,9 +719,11 @@
}
@Override
- @IntRange(from = 1)
+ @IntRange(from = 0)
public int getMaxTorchStrengthLevel() {
- return mCameraCharacteristicsCompat.getMaxTorchStrengthLevel();
+ return mCameraCharacteristicsCompat.isTorchStrengthLevelSupported()
+ ? mCameraCharacteristicsCompat.getMaxTorchStrengthLevel()
+ : TORCH_STRENGTH_LEVEL_UNSUPPORTED;
}
@Override
@@ -730,7 +732,9 @@
if (mCamera2CameraControlImpl == null) {
if (mRedirectTorchStrengthLiveData == null) {
mRedirectTorchStrengthLiveData = new RedirectableLiveData<>(
- mCameraCharacteristicsCompat.getDefaultTorchStrengthLevel());
+ mCameraCharacteristicsCompat.isTorchStrengthLevelSupported()
+ ? mCameraCharacteristicsCompat.getDefaultTorchStrengthLevel()
+ : TORCH_STRENGTH_LEVEL_UNSUPPORTED);
}
return mRedirectTorchStrengthLiveData;
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TorchControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TorchControl.java
index 7ea9db9..1bb729d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TorchControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TorchControl.java
@@ -103,10 +103,10 @@
mExecutor = executor;
mHasFlashUnit = FlashAvailabilityChecker.isFlashAvailable(cameraCharacteristics::get);
- mIsTorchStrengthSupported =
- mHasFlashUnit && Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
- && cameraCharacteristics.getMaxTorchStrengthLevel() > 1;
- mDefaultTorchStrength = cameraCharacteristics.getDefaultTorchStrengthLevel();
+ mIsTorchStrengthSupported = cameraCharacteristics.isTorchStrengthLevelSupported();
+ mDefaultTorchStrength = mHasFlashUnit && mIsTorchStrengthSupported
+ ? cameraCharacteristics.getDefaultTorchStrengthLevel()
+ : Camera2CameraInfoImpl.TORCH_STRENGTH_LEVEL_UNSUPPORTED;
mTargetTorchStrength = mDefaultTorchStrength;
mTorchState = new MutableLiveData<>(DEFAULT_TORCH_STATE);
mTorchStrength = new MutableLiveData<>(mDefaultTorchStrength);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
index 1f2cc60..766cfec 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
@@ -22,6 +22,7 @@
import android.os.Build;
import androidx.annotation.GuardedBy;
+import androidx.annotation.IntRange;
import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.internal.compat.workaround.OutputSizesCorrector;
@@ -135,23 +136,40 @@
*/
public int getDefaultTorchStrengthLevel() {
Integer defaultLevel = null;
- if (Build.VERSION.SDK_INT >= 35) {
+ if (hasFlashUnit() && Build.VERSION.SDK_INT >= 35) {
defaultLevel = get(CameraCharacteristics.FLASH_TORCH_STRENGTH_DEFAULT_LEVEL);
}
+ // The framework returns 1 when the device doesn't support configuring torch strength. So
+ // also return 1 if the device doesn't have flash unit or is unable to provide the
+ // information.
return defaultLevel == null ? 1 : defaultLevel;
}
/**
* Returns the maximum torch strength level.
*/
+ @IntRange(from = 1)
public int getMaxTorchStrengthLevel() {
Integer maxLevel = null;
- if (Build.VERSION.SDK_INT >= 35) {
+ if (hasFlashUnit() && Build.VERSION.SDK_INT >= 35) {
maxLevel = get(CameraCharacteristics.FLASH_TORCH_STRENGTH_MAX_LEVEL);
}
+ // The framework returns 1 when the device doesn't support configuring torch strength. So
+ // also return 1 if the device doesn't have flash unit or is unable to provide the
+ // information.
return maxLevel == null ? 1 : maxLevel;
}
+ public boolean isTorchStrengthLevelSupported() {
+ return hasFlashUnit() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ && getMaxTorchStrengthLevel() > 1;
+ }
+
+ private boolean hasFlashUnit() {
+ Boolean flashInfoAvailable = get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+ return flashInfoAvailable != null && flashInfoAvailable;
+ }
+
/**
* Obtains the {@link StreamConfigurationMapCompat} which contains the output sizes related
* workarounds in it.
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
index e7fcc70..1d87d00 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
@@ -916,6 +916,30 @@
assertThat(cameraInfo.getMaxTorchStrengthLevel()).isEqualTo(CAMERA0_MAX_TORCH_STRENGTH);
}
+ @Config(minSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ public void apiVersionMet_canReturnMaxTorchStrengthUnsupported()
+ throws CameraAccessExceptionCompat {
+ init(/* hasAvailableCapabilities = */ true);
+
+ final CameraInfo cameraInfo = new Camera2CameraInfoImpl(CAMERA1_ID, mCameraManagerCompat);
+
+ assertThat(cameraInfo.getMaxTorchStrengthLevel()).isEqualTo(
+ CameraInfo.TORCH_STRENGTH_LEVEL_UNSUPPORTED);
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ public void apiVersionMet_canReturnTorchStrengthUnsupported()
+ throws CameraAccessExceptionCompat {
+ init(/* hasAvailableCapabilities = */ true);
+
+ final CameraInfo cameraInfo = new Camera2CameraInfoImpl(CAMERA1_ID, mCameraManagerCompat);
+
+ assertThat(cameraInfo.getTorchStrengthLevel().getValue()).isEqualTo(
+ CameraInfo.TORCH_STRENGTH_LEVEL_UNSUPPORTED);
+ }
+
@Config(minSdk = 33)
@Test
public void apiVersionMet_canReturnSupportedDynamicRanges_fromFullySpecified()
@@ -944,13 +968,14 @@
@Config(maxSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM - 1)
@Test
- public void apiVersionNotMet_returnMaxTorchStrengthOne()
+ public void apiVersionNotMet_returnMaxTorchStrengthUnsupported()
throws CameraAccessExceptionCompat {
init(/* hasAvailableCapabilities = */ true);
final CameraInfo cameraInfo = new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
- assertThat(cameraInfo.getMaxTorchStrengthLevel()).isEqualTo(1);
+ assertThat(cameraInfo.getMaxTorchStrengthLevel()).isEqualTo(
+ CameraInfo.TORCH_STRENGTH_LEVEL_UNSUPPORTED);
}
@Config(maxSdk = 32)
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/TorchControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/TorchControlTest.java
index d725f7f..9f45cd9 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/TorchControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/TorchControlTest.java
@@ -155,6 +155,17 @@
}
@Test
+ public void setTorchStrengthLevel_throwExceptionWhenNoFlashUnit() throws InterruptedException {
+ Throwable cause = null;
+ try {
+ mNoFlashUnitTorchControl.setTorchStrengthLevel(1).get();
+ } catch (ExecutionException e) {
+ cause = e.getCause();
+ }
+ assertThat(cause).isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
public void enableTorch_whenInactive() throws InterruptedException {
mTorchControl.setActive(false);
ListenableFuture<Void> listenableFuture = mTorchControl.enableTorch(true);
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 5c0933e..893b313 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -49,7 +49,7 @@
method @FloatRange(from=0, fromInclusive=false) public default float getIntrinsicZoomRatio();
method public default int getLensFacing();
method public default androidx.lifecycle.LiveData<java.lang.Integer!> getLowLightBoostState();
- method @IntRange(from=1) public default int getMaxTorchStrengthLevel();
+ method @IntRange(from=0) public default int getMaxTorchStrengthLevel();
method public default java.util.Set<androidx.camera.core.CameraInfo!> getPhysicalCameraInfos();
method public int getSensorRotationDegrees();
method public int getSensorRotationDegrees(int);
@@ -64,6 +64,7 @@
method @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public default boolean isZslSupported();
method public static boolean mustPlayShutterSound();
method public default java.util.Set<androidx.camera.core.DynamicRange!> querySupportedDynamicRanges(java.util.Set<androidx.camera.core.DynamicRange!>);
+ field public static final int TORCH_STRENGTH_LEVEL_UNSUPPORTED = 0; // 0x0
}
public final class CameraInfoUnavailableException extends java.lang.Exception {
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 5c0933e..893b313 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -49,7 +49,7 @@
method @FloatRange(from=0, fromInclusive=false) public default float getIntrinsicZoomRatio();
method public default int getLensFacing();
method public default androidx.lifecycle.LiveData<java.lang.Integer!> getLowLightBoostState();
- method @IntRange(from=1) public default int getMaxTorchStrengthLevel();
+ method @IntRange(from=0) public default int getMaxTorchStrengthLevel();
method public default java.util.Set<androidx.camera.core.CameraInfo!> getPhysicalCameraInfos();
method public int getSensorRotationDegrees();
method public int getSensorRotationDegrees(int);
@@ -64,6 +64,7 @@
method @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public default boolean isZslSupported();
method public static boolean mustPlayShutterSound();
method public default java.util.Set<androidx.camera.core.DynamicRange!> querySupportedDynamicRanges(java.util.Set<androidx.camera.core.DynamicRange!>);
+ field public static final int TORCH_STRENGTH_LEVEL_UNSUPPORTED = 0; // 0x0
}
public final class CameraInfoUnavailableException extends java.lang.Exception {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
index ac335d1..2332ad6 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
@@ -245,6 +245,10 @@
* {@link ListenableFuture} will fail with an {@link IllegalArgumentException} and it won't
* modify the torch strength.
*
+ * <p>If the device doesn't have a flash unit or doesn't support configuring torch strength
+ * level, the returned {@link ListenableFuture} will fail with an
+ * {@link UnsupportedOperationException}.
+ *
* @param torchStrengthLevel The desired torch strength level.
* @return a {@link ListenableFuture} that is completed when the torch strength has been
* applied.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
index 52a744f..cd2f8a2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
@@ -50,6 +50,12 @@
public interface CameraInfo {
/**
+ * The torch strength level when the device doesn't have a flash unit or doesn't support
+ * adjusting torch strength.
+ */
+ int TORCH_STRENGTH_LEVEL_UNSUPPORTED = 0;
+
+ /**
* An unknown intrinsic zoom ratio. Usually to indicate the camera is unable to provide
* necessary information to resolve its intrinsic zoom ratio.
*
@@ -429,12 +435,12 @@
/**
* Returns the maximum torch strength level.
*
- * @return The maximum strength level, or {code 1} if the device doesn't have a flash unit or
- * doesn't support configuring torch strength.
+ * @return The maximum strength level, or {@link #TORCH_STRENGTH_LEVEL_UNSUPPORTED} if the
+ * device doesn't have a flash unit or doesn't support configuring torch strength.
*/
- @IntRange(from = 1)
+ @IntRange(from = 0)
default int getMaxTorchStrengthLevel() {
- return 1;
+ return TORCH_STRENGTH_LEVEL_UNSUPPORTED;
}
/**
@@ -442,9 +448,12 @@
*
* <p>The value of the {@link LiveData} will be the default torch strength level of this
* device if {@link CameraControl#setTorchStrengthLevelAsync(int)} hasn't been called.
+ *
+ * <p>The value of the {@link LiveData} will be {@link #TORCH_STRENGTH_LEVEL_UNSUPPORTED} if
+ * the device doesn't have a flash unit or doesn't support configuring torch strength.
*/
default @NonNull LiveData<Integer> getTorchStrengthLevel() {
- return new MutableLiveData<>(1);
+ return new MutableLiveData<>(TORCH_STRENGTH_LEVEL_UNSUPPORTED);
}
@StringDef(open = true, value = {IMPLEMENTATION_TYPE_UNKNOWN,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
index 10d896a..de7943c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java
@@ -19,6 +19,7 @@
import android.util.Range;
import android.util.Size;
+import androidx.annotation.IntRange;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraState;
@@ -78,6 +79,7 @@
}
@Override
+ @IntRange(from = 0)
public int getMaxTorchStrengthLevel() {
return mCameraInfoInternal.getMaxTorchStrengthLevel();
}
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index befe1c0..c3aa6d1 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -365,13 +365,13 @@
fun getResolutionInfo_shouldMatchRecordedVideoResolution() {
// Arrange.
checkAndBindUseCases(preview, videoCapture)
+ val resolutionInfo = videoCapture.resolutionInfo!!
// Act.
val result = recordingSession.createRecording().recordAndVerify()
// Assert: the resolution of the video file should match the resolution calculated by
// rotating the cropRect specified in the ResolutionInfo.
- val resolutionInfo = videoCapture.resolutionInfo!!
val expectedResolution =
rotateSize(rectToSize(resolutionInfo.cropRect), resolutionInfo.rotationDegrees)
verifyVideoResolution(context, result.file, expectedResolution)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
index a90d80a..50eb946 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
import androidx.compose.foundation.text.input.internal.selection.FakeClipboard
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@@ -854,6 +855,38 @@
}
}
+ @Test
+ fun textField_keyEvent_functionReference() {
+ val state = mutableIntStateOf(0)
+ var handled = -1
+ val focusRequester = FocusRequester()
+ rule.setContent {
+ val stateValue = state.value
+
+ @Suppress("UNUSED_PARAMETER")
+ fun handle(key: KeyEvent): Boolean {
+ handled = stateValue
+ return true
+ }
+
+ BasicTextField(
+ value = "text",
+ onValueChange = {},
+ modifier = Modifier.focusRequester(focusRequester).testTag(tag).onKeyEvent(::handle)
+ )
+ }
+
+ rule.runOnIdle { focusRequester.requestFocus() }
+ rule.onNodeWithTag(tag).performKeyInput { pressKey(Key.A) }
+ rule.runOnIdle {
+ assertThat(handled).isEqualTo(0)
+ state.value += 1
+ }
+
+ rule.onNodeWithTag(tag).performKeyInput { pressKey(Key.A) }
+ rule.runOnIdle { assertThat(handled).isEqualTo(1) }
+ }
+
private inner class SequenceScope(
val state: TextFieldState,
val clipboard: Clipboard,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
index cba5ff1..d60c1b5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
@@ -46,7 +46,7 @@
fun Modifier.onPreviewKeyEvent(onPreviewKeyEvent: (KeyEvent) -> Boolean): Modifier =
this then KeyInputElement(onKeyEvent = null, onPreKeyEvent = onPreviewKeyEvent)
-private data class KeyInputElement(
+private class KeyInputElement(
val onKeyEvent: ((KeyEvent) -> Boolean)?,
val onPreKeyEvent: ((KeyEvent) -> Boolean)?
) : ModifierNodeElement<KeyInputNode>() {
@@ -67,6 +67,21 @@
properties["onPreviewKeyEvent"] = it
}
}
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is KeyInputElement) return false
+
+ if (onKeyEvent !== other.onKeyEvent) return false
+ if (onPreKeyEvent !== other.onPreKeyEvent) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = onKeyEvent?.hashCode() ?: 0
+ result = 31 * result + (onPreKeyEvent?.hashCode() ?: 0)
+ return result
+ }
}
private class KeyInputNode(
diff --git a/libraryversions.toml b/libraryversions.toml
index 4158de0..59c4949 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -168,7 +168,7 @@
WEAR_INPUT = "1.2.0-alpha03"
WEAR_INPUT_TESTING = "1.2.0-alpha03"
WEAR_ONGOING = "1.1.0-alpha02"
-WEAR_PHONE_INTERACTIONS = "1.1.0-alpha05"
+WEAR_PHONE_INTERACTIONS = "1.1.0-beta01"
WEAR_PROTOLAYOUT = "1.3.0-alpha07"
WEAR_REMOTE_INTERACTIONS = "1.1.0-rc01"
WEAR_TILES = "1.5.0-alpha07"
diff --git a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
index 3d44721..1dc4afe 100644
--- a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
+++ b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
@@ -853,9 +853,9 @@
private fun resetViewsAndModels(fileUri: Uri) {
if (pdfLoader != null) {
pdfLoaderCallbacks?.uri = fileUri
- paginatedView?.resetModels()
destroyContentModel()
}
+ paginatedView?.resetModels()
fastScrollView?.resetContents()
findInFileView?.resetFindInFile()
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index 222e6bd..8467ece 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -299,7 +299,15 @@
} else {
switchContentFragment(ResizeFragment(), menuItem.title)
}
- R.id.item_scroll -> switchContentFragment(ScrollFragment(), menuItem.title)
+ R.id.item_scroll ->
+ if (useCompose) {
+ switchContentFragment(
+ ScrollComposeFragment(),
+ "${menuItem.title} ${getString(R.string.compose)}"
+ )
+ } else {
+ switchContentFragment(ScrollFragment(), menuItem.title)
+ }
R.id.item_pooling_container ->
switchContentFragment(PoolingContainerFragment(), menuItem.title)
R.id.item_fullscreen ->
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ScrollComposeFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ScrollComposeFragment.kt
new file mode 100644
index 0000000..6acfb9c
--- /dev/null
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ScrollComposeFragment.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.integration.testapp
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUi
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
+
+class ScrollComposeFragment : BaseFragment() {
+
+ private var bottomBannerAdapter: SandboxedUiAdapter? by mutableStateOf(null)
+ private var scrollBannerAdapter: SandboxedUiAdapter? by mutableStateOf(null)
+
+ override fun handleLoadAdFromDrawer(
+ adType: Int,
+ mediationOption: Int,
+ drawViewabilityLayer: Boolean
+ ) {
+ currentAdType = adType
+ currentMediationOption = mediationOption
+ shouldDrawViewabilityLayer = drawViewabilityLayer
+ setAdapter()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ setAdapter()
+ return ComposeView(requireContext()).apply {
+ // Dispose of the Composition when the view's LifecycleOwner is destroyed
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ Column(
+ modifier = Modifier.fillMaxSize().padding(16.dp),
+ verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.Start
+ ) {
+ Column(modifier = Modifier.weight(0.8f).verticalScroll(rememberScrollState())) {
+ scrollBannerAdapter?.let {
+ SandboxedSdkUi(
+ it,
+ Modifier.fillMaxWidth().height(200.dp),
+ providerUiOnTop = providerUiOnTop
+ )
+ }
+ Text(stringResource(R.string.long_text), Modifier.padding(vertical = 16.dp))
+ }
+ bottomBannerAdapter?.let {
+ SandboxedSdkUi(it, Modifier.weight(0.2f), providerUiOnTop = providerUiOnTop)
+ }
+ }
+ }
+ }
+ }
+
+ private fun setAdapter() {
+ val coroutineScope = MainScope()
+ coroutineScope.launch {
+ bottomBannerAdapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(
+ getSdkApi()
+ .loadBannerAd(
+ currentAdType,
+ currentMediationOption,
+ false,
+ shouldDrawViewabilityLayer,
+ )
+ )
+ scrollBannerAdapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(
+ getSdkApi()
+ .loadBannerAd(
+ currentAdType,
+ currentMediationOption,
+ false,
+ shouldDrawViewabilityLayer,
+ )
+ )
+ }
+ }
+}
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/resources.proto b/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
index 7e04e8a..edfbff0 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
@@ -155,6 +155,9 @@
//
// If not set, the animation will play on load.</setter>
androidx.wear.protolayout.expression.proto.DynamicFloat progress = 2;
+
+ // The trigger to start the animation.
+ Trigger start_trigger = 3;
}
// An image resource, which can be used by layouts. This holds multiple
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 21886f5..da4e766 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -1309,12 +1309,14 @@
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class ResourceBuilders.AndroidLottieResourceByResId {
method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
method @RawRes public int getRawResourceId();
+ method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
}
public static final class ResourceBuilders.AndroidLottieResourceByResId.Builder {
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public ResourceBuilders.AndroidLottieResourceByResId.Builder(@RawRes int);
method public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
}
@SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
@@ -1434,6 +1436,24 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleOnceTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleOnceTrigger build();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleTrigger build();
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static interface TriggerBuilders.Trigger {
@@ -1527,6 +1547,10 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static androidx.wear.protolayout.modifiers.LayoutModifier clipTopRight(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float x, optional @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float y);
}
+ public final class BorderKt {
+ method public static androidx.wear.protolayout.modifiers.LayoutModifier border(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float width, androidx.wear.protolayout.types.LayoutColor color);
+ }
+
public final class ClickableKt {
method public static androidx.wear.protolayout.ModifiersBuilders.Clickable clickable();
method public static androidx.wear.protolayout.ModifiersBuilders.Clickable clickable(optional androidx.wear.protolayout.ActionBuilders.Action action);
@@ -1557,6 +1581,10 @@
method public static androidx.wear.protolayout.ModifiersBuilders.Modifiers toProtoLayoutModifiers(androidx.wear.protolayout.modifiers.LayoutModifier);
}
+ public final class OpacityKt {
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static androidx.wear.protolayout.modifiers.LayoutModifier opacity(androidx.wear.protolayout.modifiers.LayoutModifier, @FloatRange(from=0.0, to=1.0) float staticValue, optional androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue);
+ }
+
public final class PaddingKt {
method public static androidx.wear.protolayout.modifiers.LayoutModifier padding(androidx.wear.protolayout.modifiers.LayoutModifier, androidx.wear.protolayout.ModifiersBuilders.Padding padding);
method public static androidx.wear.protolayout.modifiers.LayoutModifier padding(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float all);
@@ -1572,6 +1600,10 @@
method public static androidx.wear.protolayout.modifiers.LayoutModifier semanticsRole(androidx.wear.protolayout.modifiers.LayoutModifier, int semanticsRole);
}
+ public final class VisibilityKt {
+ method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static androidx.wear.protolayout.modifiers.LayoutModifier visibility(androidx.wear.protolayout.modifiers.LayoutModifier, boolean staticVisibility, optional androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool? dynamicVisibility);
+ }
+
}
package androidx.wear.protolayout.types {
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 21886f5..da4e766 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -1309,12 +1309,14 @@
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class ResourceBuilders.AndroidLottieResourceByResId {
method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
method @RawRes public int getRawResourceId();
+ method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
}
public static final class ResourceBuilders.AndroidLottieResourceByResId.Builder {
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public ResourceBuilders.AndroidLottieResourceByResId.Builder(@RawRes int);
method public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
}
@SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
@@ -1434,6 +1436,24 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleOnceTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleOnceTrigger build();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleTrigger build();
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static interface TriggerBuilders.Trigger {
@@ -1527,6 +1547,10 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static androidx.wear.protolayout.modifiers.LayoutModifier clipTopRight(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float x, optional @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float y);
}
+ public final class BorderKt {
+ method public static androidx.wear.protolayout.modifiers.LayoutModifier border(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float width, androidx.wear.protolayout.types.LayoutColor color);
+ }
+
public final class ClickableKt {
method public static androidx.wear.protolayout.ModifiersBuilders.Clickable clickable();
method public static androidx.wear.protolayout.ModifiersBuilders.Clickable clickable(optional androidx.wear.protolayout.ActionBuilders.Action action);
@@ -1557,6 +1581,10 @@
method public static androidx.wear.protolayout.ModifiersBuilders.Modifiers toProtoLayoutModifiers(androidx.wear.protolayout.modifiers.LayoutModifier);
}
+ public final class OpacityKt {
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static androidx.wear.protolayout.modifiers.LayoutModifier opacity(androidx.wear.protolayout.modifiers.LayoutModifier, @FloatRange(from=0.0, to=1.0) float staticValue, optional androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue);
+ }
+
public final class PaddingKt {
method public static androidx.wear.protolayout.modifiers.LayoutModifier padding(androidx.wear.protolayout.modifiers.LayoutModifier, androidx.wear.protolayout.ModifiersBuilders.Padding padding);
method public static androidx.wear.protolayout.modifiers.LayoutModifier padding(androidx.wear.protolayout.modifiers.LayoutModifier, @Dimension(unit=androidx.annotation.Dimension.Companion.DP) float all);
@@ -1572,6 +1600,10 @@
method public static androidx.wear.protolayout.modifiers.LayoutModifier semanticsRole(androidx.wear.protolayout.modifiers.LayoutModifier, int semanticsRole);
}
+ public final class VisibilityKt {
+ method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=300) public static androidx.wear.protolayout.modifiers.LayoutModifier visibility(androidx.wear.protolayout.modifiers.LayoutModifier, boolean staticVisibility, optional androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool? dynamicVisibility);
+ }
+
}
package androidx.wear.protolayout.types {
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
index 0fff0a2..a51c996 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -520,6 +520,15 @@
}
}
+ /** Gets the trigger to start the animation. */
+ public @Nullable Trigger getStartTrigger() {
+ if (mImpl.hasStartTrigger()) {
+ return TriggerBuilders.triggerFromProto(mImpl.getStartTrigger());
+ } else {
+ return null;
+ }
+ }
+
/** Creates a new wrapper instance from the proto. */
@RestrictTo(Scope.LIBRARY_GROUP)
public static @NonNull AndroidLottieResourceByResId fromProto(
@@ -540,6 +549,8 @@
+ getRawResourceId()
+ ", progress="
+ getProgress()
+ + ", startTrigger="
+ + getStartTrigger()
+ "}";
}
@@ -590,6 +601,13 @@
return this;
}
+ /** Sets the trigger to start the animation. */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public @NonNull Builder setStartTrigger(@NonNull Trigger startTrigger) {
+ mImpl.setStartTrigger(startTrigger.toTriggerProto());
+ return this;
+ }
+
/** Builds an instance from accumulated values. */
public @NonNull AndroidLottieResourceByResId build() {
return AndroidLottieResourceByResId.fromProto(mImpl.build());
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
index 1a96d0e..d05a53c 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
@@ -48,6 +48,160 @@
return new OnConditionMetTrigger.Builder().setCondition(dynamicBool).build();
}
+ /**
+ * Creates a {@link Trigger} that fires *every time* the layout becomes visible.
+ *
+ * <p>As opposed to {@link #createOnLoadTrigger()}, this will wait until layout is fully visible
+ * before firing a trigger.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static @NonNull Trigger createOnVisibleTrigger() {
+ return new OnVisibleTrigger.Builder().build();
+ }
+
+ /**
+ * Creates a {@link Trigger} that fires the first time that layout becomes visible.
+ *
+ * <p>As opposed to {@link #createOnVisibleTrigger()}, this will only be fired the first time
+ * that the layout becomes visible.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static @NonNull Trigger createOnVisibleOnceTrigger() {
+ return new OnVisibleOnceTrigger.Builder().build();
+ }
+
+ /** Triggers when the layout visibility state turns from invisible to fully visible. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static final class OnVisibleTrigger implements Trigger {
+ private final TriggerProto.OnVisibleTrigger mImpl;
+ private final @Nullable Fingerprint mFingerprint;
+
+ OnVisibleTrigger(TriggerProto.OnVisibleTrigger impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public @Nullable Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public static @NonNull OnVisibleTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleTrigger proto, @Nullable Fingerprint fingerprint) {
+ return new OnVisibleTrigger(proto, fingerprint);
+ }
+
+ static @NonNull OnVisibleTrigger fromProto(TriggerProto.@NonNull OnVisibleTrigger proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ TriggerProto.@NonNull OnVisibleTrigger toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public TriggerProto.@NonNull Trigger toTriggerProto() {
+ return TriggerProto.Trigger.newBuilder().setOnVisibleTrigger(mImpl).build();
+ }
+
+ @Override
+ public @NonNull String toString() {
+ return "OnVisibleTrigger";
+ }
+
+ /** Builder for {@link OnVisibleTrigger}. */
+ @SuppressWarnings("HiddenSuperclass")
+ public static final class Builder implements Trigger.Builder {
+ private final TriggerProto.OnVisibleTrigger.Builder mImpl =
+ TriggerProto.OnVisibleTrigger.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(1416366796);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @Override
+ public @NonNull OnVisibleTrigger build() {
+ return new OnVisibleTrigger(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
+ /**
+ * Triggers only once when the layout visibility state turns from invisible to fully visible for
+ * the first time.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static final class OnVisibleOnceTrigger implements Trigger {
+ private final TriggerProto.OnVisibleOnceTrigger mImpl;
+ private final @Nullable Fingerprint mFingerprint;
+
+ OnVisibleOnceTrigger(
+ TriggerProto.OnVisibleOnceTrigger impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public @Nullable Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public static @NonNull OnVisibleOnceTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleOnceTrigger proto,
+ @Nullable Fingerprint fingerprint) {
+ return new OnVisibleOnceTrigger(proto, fingerprint);
+ }
+
+ static @NonNull OnVisibleOnceTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleOnceTrigger proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ TriggerProto.@NonNull OnVisibleOnceTrigger toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public TriggerProto.@NonNull Trigger toTriggerProto() {
+ return TriggerProto.Trigger.newBuilder().setOnVisibleOnceTrigger(mImpl).build();
+ }
+
+ @Override
+ public @NonNull String toString() {
+ return "OnVisibleOnceTrigger";
+ }
+
+ /** Builder for {@link OnVisibleOnceTrigger}. */
+ @SuppressWarnings("HiddenSuperclass")
+ public static final class Builder implements Trigger.Builder {
+ private final TriggerProto.OnVisibleOnceTrigger.Builder mImpl =
+ TriggerProto.OnVisibleOnceTrigger.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(-1661457257);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @Override
+ public @NonNull OnVisibleOnceTrigger build() {
+ return new OnVisibleOnceTrigger(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
/** Triggers immediately when the layout is loaded / reloaded. */
@RequiresSchemaVersion(major = 1, minor = 200)
static final class OnLoadTrigger implements Trigger {
@@ -225,6 +379,12 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public static @NonNull Trigger triggerFromProto(
TriggerProto.@NonNull Trigger proto, @Nullable Fingerprint fingerprint) {
+ if (proto.hasOnVisibleTrigger()) {
+ return OnVisibleTrigger.fromProto(proto.getOnVisibleTrigger(), fingerprint);
+ }
+ if (proto.hasOnVisibleOnceTrigger()) {
+ return OnVisibleOnceTrigger.fromProto(proto.getOnVisibleOnceTrigger(), fingerprint);
+ }
if (proto.hasOnLoadTrigger()) {
return OnLoadTrigger.fromProto(proto.getOnLoadTrigger(), fingerprint);
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt
new file mode 100644
index 0000000..d3fca75
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.modifiers
+
+import androidx.annotation.Dimension
+import androidx.annotation.Dimension.Companion.DP
+import androidx.wear.protolayout.ModifiersBuilders.Border
+import androidx.wear.protolayout.types.LayoutColor
+import androidx.wear.protolayout.types.dp
+
+/**
+ * Adds a modifier to apply a border around an element.
+ *
+ * @param width The width of the border, in `DP`.
+ * @param color The color of the border.
+ */
+fun LayoutModifier.border(@Dimension(DP) width: Float, color: LayoutColor): LayoutModifier =
+ this then BaseBorderElement(width, color)
+
+internal class BaseBorderElement(@Dimension(DP) val width: Float, val color: LayoutColor) :
+ LayoutModifier.Element {
+ fun foldIn(initial: Border.Builder?): Border.Builder =
+ (initial ?: Border.Builder()).setWidth(width.dp).setColor(color.prop)
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
index 0b5531a..aee3599 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
@@ -16,15 +16,23 @@
package androidx.wear.protolayout.modifiers
+import android.annotation.SuppressLint
+import androidx.annotation.OptIn
import androidx.wear.protolayout.ModifiersBuilders
import androidx.wear.protolayout.ModifiersBuilders.Background
+import androidx.wear.protolayout.ModifiersBuilders.Border
import androidx.wear.protolayout.ModifiersBuilders.Clickable
import androidx.wear.protolayout.ModifiersBuilders.Corner
import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata
import androidx.wear.protolayout.ModifiersBuilders.Padding
import androidx.wear.protolayout.ModifiersBuilders.Semantics
+import androidx.wear.protolayout.TypeBuilders.BoolProp
+import androidx.wear.protolayout.TypeBuilders.FloatProp
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental
/** Creates a [ModifiersBuilders.Modifiers] from a [LayoutModifier]. */
+@SuppressLint("ProtoLayoutMinSchema")
+@OptIn(ProtoLayoutExperimental::class)
fun LayoutModifier.toProtoLayoutModifiers(): ModifiersBuilders.Modifiers {
var semantics: Semantics.Builder? = null
var background: Background.Builder? = null
@@ -32,6 +40,9 @@
var clickable: Clickable.Builder? = null
var padding: Padding.Builder? = null
var metadata: ElementMetadata.Builder? = null
+ var border: Border.Builder? = null
+ var visible: BoolProp.Builder? = null
+ var opacity: FloatProp.Builder? = null
this.foldIn(Unit) { _, e ->
when (e) {
@@ -41,6 +52,9 @@
is BaseClickableElement -> clickable = e.foldIn(clickable)
is BasePaddingElement -> padding = e.foldIn(padding)
is BaseMetadataElement -> metadata = e.foldIn(metadata)
+ is BaseBorderElement -> border = e.foldIn(border)
+ is BaseVisibilityElement -> visible = e.foldIn(visible)
+ is BaseOpacityElement -> opacity = e.foldIn(opacity)
}
}
@@ -53,6 +67,9 @@
clickable?.let { setClickable(it.build()) }
padding?.let { setPadding(it.build()) }
metadata?.let { setMetadata(it.build()) }
+ border?.let { setBorder(it.build()) }
+ visible?.let { setVisible(it.build()) }
+ opacity?.let { setOpacity(it.build()) }
}
.build()
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt
new file mode 100644
index 0000000..eeafb8f9
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.modifiers
+
+import android.annotation.SuppressLint
+import androidx.annotation.FloatRange
+import androidx.wear.protolayout.TypeBuilders.FloatProp
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
+import androidx.wear.protolayout.expression.RequiresSchemaVersion
+
+/**
+ * Adds a modifier to specify the opacity of the element with a value from 0 to 1, where 0 means the
+ * element is completely transparent and 1 means the element is completely opaque.
+ *
+ * @param staticValue The static value for opacity. This value will be used if [dynamicValue] is
+ * null, or if can't be resolved.
+ * @param dynamicValue The dynamic value for opacity. This can be used to change the opacity of the
+ * element dynamically (without changing the layout definition). To create a smooth transition for
+ * the dynamic change, you can use one of [DynamicFloat.animate] methods.
+ */
+@RequiresSchemaVersion(major = 1, minor = 400)
+fun LayoutModifier.opacity(
+ @FloatRange(from = 0.0, to = 1.0) staticValue: Float,
+ dynamicValue: DynamicFloat? = null
+): LayoutModifier = this then BaseOpacityElement(staticValue, dynamicValue)
+
+@RequiresSchemaVersion(major = 1, minor = 400)
+internal class BaseOpacityElement(val staticValue: Float, val dynamicValue: DynamicFloat? = null) :
+ LayoutModifier.Element {
+ @SuppressLint("ProtoLayoutMinSchema")
+ fun foldIn(initial: FloatProp.Builder?): FloatProp.Builder =
+ (initial ?: FloatProp.Builder(staticValue)).apply {
+ dynamicValue?.let { setDynamicValue(it) }
+ }
+}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt
new file mode 100644
index 0000000..b7eabd7
--- /dev/null
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.modifiers
+
+import android.annotation.SuppressLint
+import androidx.wear.protolayout.TypeBuilders.BoolProp
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental
+import androidx.wear.protolayout.expression.RequiresSchemaVersion
+
+/**
+ * Adds a modifier to specify the visibility of the element. A hidden element still consume space in
+ * the layout, but will not render any contents, nor will any of its children render any contents.
+ *
+ * Note that hidden elements won't receive input events.
+ *
+ * @param staticVisibility The static value for visibility. This value will be used if
+ * [dynamicVisibility] is null, or if can't be resolved.
+ * @param dynamicVisibility The dynamic value for visibility. This can be used to change the
+ * visibility of the element dynamically (without changing the layout definition).
+ */
+@RequiresSchemaVersion(major = 1, minor = 300)
+@ProtoLayoutExperimental
+fun LayoutModifier.visibility(
+ staticVisibility: Boolean,
+ dynamicVisibility: DynamicBool? = null
+): LayoutModifier = this then BaseVisibilityElement(staticVisibility, dynamicVisibility)
+
+@RequiresSchemaVersion(major = 1, minor = 300)
+internal class BaseVisibilityElement(
+ val visibility: Boolean,
+ val dynamicVisibility: DynamicBool? = null
+) : LayoutModifier.Element {
+ @SuppressLint("ProtoLayoutMinSchema")
+ fun foldIn(initial: BoolProp.Builder?): BoolProp.Builder =
+ (initial ?: BoolProp.Builder(visibility)).apply {
+ dynamicVisibility?.let { setDynamicValue(it) }
+ }
+}
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
index 3591875..c0256c3 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
@@ -74,9 +74,25 @@
.setProgress(DynamicBuilders.DynamicFloat.from(new AppDataKey<>(stateKey)))
.build();
- ResourceProto.AndroidLottieResourceByResId avdProto = lottieResource.toProto();
+ ResourceProto.AndroidLottieResourceByResId lottieProto = lottieResource.toProto();
- assertThat(avdProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
- assertThat(avdProto.getProgress().getStateSource().getSourceKey()).isEqualTo(stateKey);
+ assertThat(lottieProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
+ assertThat(lottieProto.getProgress().getStateSource().getSourceKey()).isEqualTo(stateKey);
+ }
+
+ @Test
+ public void lottieAnimation_hasTrigger() {
+ ResourceBuilders.AndroidLottieResourceByResId lottieResource =
+ new ResourceBuilders.AndroidLottieResourceByResId.Builder(RESOURCE_ID)
+ .setStartTrigger(TriggerBuilders.createOnVisibleTrigger())
+ .build();
+
+ ResourceProto.AndroidLottieResourceByResId lottieProto = lottieResource.toProto();
+
+ assertThat(lottieProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
+ assertThat(lottieProto.hasStartTrigger()).isTrue();
+ assertThat(lottieProto.getStartTrigger().hasOnVisibleTrigger()).isTrue();
+ assertThat(lottieProto.getStartTrigger().hasOnVisibleOnceTrigger()).isFalse();
+ assertThat(lottieProto.getStartTrigger().hasOnLoadTrigger()).isFalse();
}
}
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/modifiers/ModifiersTest.kt b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/modifiers/ModifiersTest.kt
index 80f8b5e..ca402dc 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/modifiers/ModifiersTest.kt
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/modifiers/ModifiersTest.kt
@@ -24,6 +24,7 @@
import androidx.wear.protolayout.ModifiersBuilders.SEMANTICS_ROLE_BUTTON
import androidx.wear.protolayout.ModifiersBuilders.SEMANTICS_ROLE_NONE
import androidx.wear.protolayout.expression.AppDataKey
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import androidx.wear.protolayout.expression.DynamicDataBuilders.DynamicDataValue
@@ -241,6 +242,26 @@
assertThat(modifiers.metadata?.tagData).isEqualTo(METADATA_BYTE_ARRAY)
}
+ @Test
+ fun border_toModifier() {
+ val modifier =
+ LayoutModifier.border(width = WIDTH_DP, color = COLOR).toProtoLayoutModifiers()
+
+ assertThat(modifier.border?.width?.value).isEqualTo(WIDTH_DP)
+ assertThat(modifier.border?.color?.argb).isEqualTo(COLOR.prop.argb)
+ }
+
+ @Test
+ fun visibility_toModifier() {
+ val modifier =
+ LayoutModifier.visibility(staticVisibility = false, dynamicVisibility = DYNAMIC_BOOL)
+ .toProtoLayoutModifiers()
+
+ assertThat(modifier.isVisible.value).isEqualTo(false)
+ assertThat(modifier.isVisible.dynamicValue?.toDynamicBoolProto())
+ .isEqualTo(DYNAMIC_BOOL.toDynamicBoolProto())
+ }
+
companion object {
const val STATIC_CONTENT_DESCRIPTION = "content desc"
val DYNAMIC_CONTENT_DESCRIPTION = DynamicString.constant("dynamic content")
@@ -255,5 +276,7 @@
const val PADDING_ALL = 5f
const val METADATA = "metadata"
val METADATA_BYTE_ARRAY = METADATA.toByteArray()
+ const val WIDTH_DP = 5f
+ val DYNAMIC_BOOL = DynamicBool.constant(true)
}
}
diff --git a/wear/wear-phone-interactions/api/1.1.0-beta01.txt b/wear/wear-phone-interactions/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..22d6f21
--- /dev/null
+++ b/wear/wear-phone-interactions/api/1.1.0-beta01.txt
@@ -0,0 +1,170 @@
+// Signature format: 4.0
+package androidx.wear.phone.interactions {
+
+ public final class PhoneTypeHelper {
+ method public static int getPhoneDeviceType(android.content.Context context);
+ field public static final androidx.wear.phone.interactions.PhoneTypeHelper.Companion Companion;
+ field public static final int DEVICE_TYPE_ANDROID = 1; // 0x1
+ field public static final int DEVICE_TYPE_ERROR = 0; // 0x0
+ field public static final int DEVICE_TYPE_IOS = 2; // 0x2
+ field public static final int DEVICE_TYPE_NONE = 4; // 0x4
+ field public static final int DEVICE_TYPE_UNKNOWN = 3; // 0x3
+ }
+
+ public static final class PhoneTypeHelper.Companion {
+ method public int getPhoneDeviceType(android.content.Context context);
+ property public static final int DEVICE_TYPE_ANDROID;
+ property public static final int DEVICE_TYPE_ERROR;
+ property public static final int DEVICE_TYPE_IOS;
+ property public static final int DEVICE_TYPE_NONE;
+ property public static final int DEVICE_TYPE_UNKNOWN;
+ }
+
+}
+
+package androidx.wear.phone.interactions.authentication {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CodeChallenge {
+ ctor public CodeChallenge(androidx.wear.phone.interactions.authentication.CodeVerifier codeVerifier);
+ method public String getValue();
+ property public final String value;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CodeVerifier {
+ ctor public CodeVerifier();
+ ctor public CodeVerifier(optional int byteLength);
+ ctor public CodeVerifier(String value);
+ method public String getValue();
+ property public final String value;
+ }
+
+ public final class OAuthRequest {
+ method public String getPackageName();
+ method public String getRedirectUrl();
+ method public android.net.Uri getRequestUrl();
+ property public final String packageName;
+ property public final String redirectUrl;
+ property public final android.net.Uri requestUrl;
+ field public static final androidx.wear.phone.interactions.authentication.OAuthRequest.Companion Companion;
+ field public static final String WEAR_REDIRECT_URL_PREFIX = "https://wear.googleapis.com/3p_auth/";
+ field public static final String WEAR_REDIRECT_URL_PREFIX_CN = "https://wear.googleapis-cn.com/3p_auth/";
+ }
+
+ public static final class OAuthRequest.Builder {
+ ctor public OAuthRequest.Builder(android.content.Context context);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public androidx.wear.phone.interactions.authentication.OAuthRequest build();
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setAuthProviderUrl(android.net.Uri authProviderUrl);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setClientId(String clientId);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setCodeChallenge(androidx.wear.phone.interactions.authentication.CodeChallenge codeChallenge);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setRedirectUrl(android.net.Uri redirectUrl);
+ }
+
+ public static final class OAuthRequest.Companion {
+ property public static final String WEAR_REDIRECT_URL_PREFIX;
+ property public static final String WEAR_REDIRECT_URL_PREFIX_CN;
+ }
+
+ public final class OAuthResponse {
+ method public int getErrorCode();
+ method public android.net.Uri? getResponseUrl();
+ property public final int errorCode;
+ property public final android.net.Uri? responseUrl;
+ }
+
+ public static final class OAuthResponse.Builder {
+ ctor public OAuthResponse.Builder();
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse build();
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse.Builder setErrorCode(int errorCode);
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse.Builder setResponseUrl(android.net.Uri responseUrl);
+ }
+
+ public final class RemoteAuthClient implements java.lang.AutoCloseable {
+ method @UiThread public void close();
+ method public static androidx.wear.phone.interactions.authentication.RemoteAuthClient create(android.content.Context context);
+ method protected void finalize();
+ method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getAvailabilityStatus();
+ method @UiThread public void sendAuthorizationRequest(androidx.wear.phone.interactions.authentication.OAuthRequest request, java.util.concurrent.Executor executor, androidx.wear.phone.interactions.authentication.RemoteAuthClient.Callback clientCallback);
+ property public final kotlinx.coroutines.flow.Flow<java.lang.Integer> availabilityStatus;
+ field public static final androidx.wear.phone.interactions.authentication.RemoteAuthClient.Companion Companion;
+ field public static final int ERROR_PHONE_UNAVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNSUPPORTED = 0; // 0x0
+ field public static final int NO_ERROR = -1; // 0xffffffff
+ field public static final int STATUS_AVAILABLE = 3; // 0x3
+ field public static final int STATUS_TEMPORARILY_UNAVAILABLE = 2; // 0x2
+ field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public abstract static class RemoteAuthClient.Callback {
+ ctor public RemoteAuthClient.Callback();
+ method @UiThread public abstract void onAuthorizationError(androidx.wear.phone.interactions.authentication.OAuthRequest request, int errorCode);
+ method @UiThread public abstract void onAuthorizationResponse(androidx.wear.phone.interactions.authentication.OAuthRequest request, androidx.wear.phone.interactions.authentication.OAuthResponse response);
+ }
+
+ public static final class RemoteAuthClient.Companion {
+ method public androidx.wear.phone.interactions.authentication.RemoteAuthClient create(android.content.Context context);
+ property public static final int ERROR_PHONE_UNAVAILABLE;
+ property public static final int ERROR_UNSUPPORTED;
+ property public static final int NO_ERROR;
+ property public static final int STATUS_AVAILABLE;
+ property public static final int STATUS_TEMPORARILY_UNAVAILABLE;
+ property public static final int STATUS_UNAVAILABLE;
+ property public static final int STATUS_UNKNOWN;
+ }
+
+ public interface RemoteAuthRequestHandler {
+ method public boolean isAuthSupported();
+ method public void sendAuthRequest(androidx.wear.phone.interactions.authentication.OAuthRequest request, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ }
+
+ public abstract class RemoteAuthService extends android.app.Service {
+ ctor public RemoteAuthService();
+ method protected final android.os.IBinder onBind(android.content.Intent intent, androidx.wear.phone.interactions.authentication.RemoteAuthRequestHandler remoteAuthRequestHandler);
+ method public static final void sendResponseToCallback(androidx.wear.phone.interactions.authentication.OAuthResponse response, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ method protected boolean verifyPackageName(android.content.Context context, String? requestPackageName);
+ field public static final androidx.wear.phone.interactions.authentication.RemoteAuthService.Companion Companion;
+ }
+
+ public static final class RemoteAuthService.Companion {
+ method public void sendResponseToCallback(androidx.wear.phone.interactions.authentication.OAuthResponse response, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ }
+
+}
+
+package androidx.wear.phone.interactions.notifications {
+
+ public final class BridgingConfig {
+ method public java.util.Set<java.lang.String>? getExcludedTags();
+ method public boolean isBridgingEnabled();
+ property public final java.util.Set<java.lang.String>? excludedTags;
+ property public final boolean isBridgingEnabled;
+ }
+
+ public static final class BridgingConfig.Builder {
+ ctor public BridgingConfig.Builder(android.content.Context context, boolean isBridgingEnabled);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTag(String tag);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTags(java.util.Collection<java.lang.String> tags);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig build();
+ }
+
+ public fun interface BridgingConfigurationHandler {
+ method public void applyBridgingConfiguration(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ }
+
+ public final class BridgingManager {
+ method public static androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ method public void setConfig(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ field public static final androidx.wear.phone.interactions.notifications.BridgingManager.Companion Companion;
+ }
+
+ public static final class BridgingManager.Companion {
+ method public androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ }
+
+ public final class BridgingManagerService extends android.app.Service {
+ ctor public BridgingManagerService(android.content.Context context, androidx.wear.phone.interactions.notifications.BridgingConfigurationHandler bridgingConfigurationHandler);
+ method public android.os.IBinder? onBind(android.content.Intent? intent);
+ }
+
+}
+
diff --git a/wear/wear-phone-interactions/api/res-1.1.0-beta01.txt b/wear/wear-phone-interactions/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/wear/wear-phone-interactions/api/res-1.1.0-beta01.txt
diff --git a/wear/wear-phone-interactions/api/restricted_1.1.0-beta01.txt b/wear/wear-phone-interactions/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..0798d72
--- /dev/null
+++ b/wear/wear-phone-interactions/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,173 @@
+// Signature format: 4.0
+package androidx.wear.phone.interactions {
+
+ public final class PhoneTypeHelper {
+ method public static int getPhoneDeviceType(android.content.Context context);
+ field public static final androidx.wear.phone.interactions.PhoneTypeHelper.Companion Companion;
+ field public static final int DEVICE_TYPE_ANDROID = 1; // 0x1
+ field public static final int DEVICE_TYPE_ERROR = 0; // 0x0
+ field public static final int DEVICE_TYPE_IOS = 2; // 0x2
+ field public static final int DEVICE_TYPE_NONE = 4; // 0x4
+ field public static final int DEVICE_TYPE_UNKNOWN = 3; // 0x3
+ }
+
+ public static final class PhoneTypeHelper.Companion {
+ method public int getPhoneDeviceType(android.content.Context context);
+ property public static final int DEVICE_TYPE_ANDROID;
+ property public static final int DEVICE_TYPE_ERROR;
+ property public static final int DEVICE_TYPE_IOS;
+ property public static final int DEVICE_TYPE_NONE;
+ property public static final int DEVICE_TYPE_UNKNOWN;
+ }
+
+}
+
+package androidx.wear.phone.interactions.authentication {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CodeChallenge {
+ ctor public CodeChallenge(androidx.wear.phone.interactions.authentication.CodeVerifier codeVerifier);
+ method public String getValue();
+ property public final String value;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class CodeVerifier {
+ ctor public CodeVerifier();
+ ctor public CodeVerifier(optional int byteLength);
+ ctor public CodeVerifier(String value);
+ method public String getValue();
+ property public final String value;
+ }
+
+ public final class OAuthRequest {
+ method public String getPackageName();
+ method public String getRedirectUrl();
+ method public android.net.Uri getRequestUrl();
+ property public final String packageName;
+ property public final String redirectUrl;
+ property public final android.net.Uri requestUrl;
+ field public static final androidx.wear.phone.interactions.authentication.OAuthRequest.Companion Companion;
+ field public static final String WEAR_REDIRECT_URL_PREFIX = "https://wear.googleapis.com/3p_auth/";
+ field public static final String WEAR_REDIRECT_URL_PREFIX_CN = "https://wear.googleapis-cn.com/3p_auth/";
+ }
+
+ public static final class OAuthRequest.Builder {
+ ctor public OAuthRequest.Builder(android.content.Context context);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O) public androidx.wear.phone.interactions.authentication.OAuthRequest build();
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setAuthProviderUrl(android.net.Uri authProviderUrl);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setClientId(String clientId);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setCodeChallenge(androidx.wear.phone.interactions.authentication.CodeChallenge codeChallenge);
+ method public androidx.wear.phone.interactions.authentication.OAuthRequest.Builder setRedirectUrl(android.net.Uri redirectUrl);
+ }
+
+ public static final class OAuthRequest.Companion {
+ property public static final String WEAR_REDIRECT_URL_PREFIX;
+ property public static final String WEAR_REDIRECT_URL_PREFIX_CN;
+ }
+
+ public final class OAuthResponse {
+ method public int getErrorCode();
+ method public android.net.Uri? getResponseUrl();
+ property @androidx.wear.phone.interactions.authentication.RemoteAuthClient.Companion.ErrorCode public final int errorCode;
+ property public final android.net.Uri? responseUrl;
+ }
+
+ public static final class OAuthResponse.Builder {
+ ctor public OAuthResponse.Builder();
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse build();
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse.Builder setErrorCode(@androidx.wear.phone.interactions.authentication.RemoteAuthClient.Companion.ErrorCode int errorCode);
+ method public androidx.wear.phone.interactions.authentication.OAuthResponse.Builder setResponseUrl(android.net.Uri responseUrl);
+ }
+
+ public final class RemoteAuthClient implements java.lang.AutoCloseable {
+ method @UiThread public void close();
+ method public static androidx.wear.phone.interactions.authentication.RemoteAuthClient create(android.content.Context context);
+ method protected void finalize();
+ method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getAvailabilityStatus();
+ method @UiThread public void sendAuthorizationRequest(androidx.wear.phone.interactions.authentication.OAuthRequest request, java.util.concurrent.Executor executor, androidx.wear.phone.interactions.authentication.RemoteAuthClient.Callback clientCallback);
+ property public final kotlinx.coroutines.flow.Flow<java.lang.Integer> availabilityStatus;
+ field public static final androidx.wear.phone.interactions.authentication.RemoteAuthClient.Companion Companion;
+ field public static final int ERROR_PHONE_UNAVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNSUPPORTED = 0; // 0x0
+ field public static final int NO_ERROR = -1; // 0xffffffff
+ field public static final int STATUS_AVAILABLE = 3; // 0x3
+ field public static final int STATUS_TEMPORARILY_UNAVAILABLE = 2; // 0x2
+ field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public abstract static class RemoteAuthClient.Callback {
+ ctor public RemoteAuthClient.Callback();
+ method @UiThread public abstract void onAuthorizationError(androidx.wear.phone.interactions.authentication.OAuthRequest request, @androidx.wear.phone.interactions.authentication.RemoteAuthClient.Companion.ErrorCode int errorCode);
+ method @UiThread public abstract void onAuthorizationResponse(androidx.wear.phone.interactions.authentication.OAuthRequest request, androidx.wear.phone.interactions.authentication.OAuthResponse response);
+ }
+
+ public static final class RemoteAuthClient.Companion {
+ method public androidx.wear.phone.interactions.authentication.RemoteAuthClient create(android.content.Context context);
+ property public static final int ERROR_PHONE_UNAVAILABLE;
+ property public static final int ERROR_UNSUPPORTED;
+ property public static final int NO_ERROR;
+ property public static final int STATUS_AVAILABLE;
+ property public static final int STATUS_TEMPORARILY_UNAVAILABLE;
+ property public static final int STATUS_UNAVAILABLE;
+ property public static final int STATUS_UNKNOWN;
+ }
+
+ @IntDef({androidx.wear.phone.interactions.authentication.RemoteAuthClient.NO_ERROR, androidx.wear.phone.interactions.authentication.RemoteAuthClient.ERROR_UNSUPPORTED, androidx.wear.phone.interactions.authentication.RemoteAuthClient.ERROR_PHONE_UNAVAILABLE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) public static @interface RemoteAuthClient.Companion.ErrorCode {
+ }
+
+ public interface RemoteAuthRequestHandler {
+ method public boolean isAuthSupported();
+ method public void sendAuthRequest(androidx.wear.phone.interactions.authentication.OAuthRequest request, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ }
+
+ public abstract class RemoteAuthService extends android.app.Service {
+ ctor public RemoteAuthService();
+ method protected final android.os.IBinder onBind(android.content.Intent intent, androidx.wear.phone.interactions.authentication.RemoteAuthRequestHandler remoteAuthRequestHandler);
+ method public static final void sendResponseToCallback(androidx.wear.phone.interactions.authentication.OAuthResponse response, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ method protected boolean verifyPackageName(android.content.Context context, String? requestPackageName);
+ field public static final androidx.wear.phone.interactions.authentication.RemoteAuthService.Companion Companion;
+ }
+
+ public static final class RemoteAuthService.Companion {
+ method public void sendResponseToCallback(androidx.wear.phone.interactions.authentication.OAuthResponse response, kotlin.Pair<java.lang.String,java.lang.Integer> packageNameAndRequestId);
+ }
+
+}
+
+package androidx.wear.phone.interactions.notifications {
+
+ public final class BridgingConfig {
+ method public java.util.Set<java.lang.String>? getExcludedTags();
+ method public boolean isBridgingEnabled();
+ property public final java.util.Set<java.lang.String>? excludedTags;
+ property public final boolean isBridgingEnabled;
+ }
+
+ public static final class BridgingConfig.Builder {
+ ctor public BridgingConfig.Builder(android.content.Context context, boolean isBridgingEnabled);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTag(String tag);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig.Builder addExcludedTags(java.util.Collection<java.lang.String> tags);
+ method public androidx.wear.phone.interactions.notifications.BridgingConfig build();
+ }
+
+ public fun interface BridgingConfigurationHandler {
+ method public void applyBridgingConfiguration(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ }
+
+ public final class BridgingManager {
+ method public static androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ method public void setConfig(androidx.wear.phone.interactions.notifications.BridgingConfig bridgingConfig);
+ field public static final androidx.wear.phone.interactions.notifications.BridgingManager.Companion Companion;
+ }
+
+ public static final class BridgingManager.Companion {
+ method public androidx.wear.phone.interactions.notifications.BridgingManager fromContext(android.content.Context context);
+ }
+
+ public final class BridgingManagerService extends android.app.Service {
+ ctor public BridgingManagerService(android.content.Context context, androidx.wear.phone.interactions.notifications.BridgingConfigurationHandler bridgingConfigurationHandler);
+ method public android.os.IBinder? onBind(android.content.Intent? intent);
+ }
+
+}
+
diff --git a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/PhoneTypeHelper.kt b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/PhoneTypeHelper.kt
index e28ae0c..676a0ca 100644
--- a/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/PhoneTypeHelper.kt
+++ b/wear/wear-phone-interactions/src/main/java/androidx/wear/phone/interactions/PhoneTypeHelper.kt
@@ -34,10 +34,14 @@
.path(BLUETOOTH_MODE)
.build()
+ /**
+ * These values follow the values of platform constants defined in
+ * [Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE].
+ */
internal const val UNKNOWN_MODE = 0
internal const val ANDROID_MODE = 1
internal const val IOS_MODE = 2
- internal const val NONE_PAIRED_MODE = 4
+ internal const val NONE_PAIRED_MODE = 3
/** Indicates an error returned retrieving the type of phone we are paired to. */
public const val DEVICE_TYPE_ERROR: Int = 0
@@ -66,7 +70,7 @@
@DeviceFamily
@JvmStatic
public fun getPhoneDeviceType(context: Context): Int {
- var bluetoothMode = UNKNOWN_MODE
+ var pairedDeviceType = UNKNOWN_MODE
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
val cursor =
context.contentResolver.query(BLUETOOTH_MODE_URI, null, null, null, null)
@@ -74,25 +78,25 @@
cursor.use {
while (it.moveToNext()) {
if (BLUETOOTH_MODE == it.getString(0)) {
- bluetoothMode = it.getInt(1)
+ pairedDeviceType = it.getInt(1)
break
}
}
}
+ return when (pairedDeviceType) {
+ ANDROID_MODE -> DEVICE_TYPE_ANDROID
+ IOS_MODE -> DEVICE_TYPE_IOS
+ else -> DEVICE_TYPE_UNKNOWN
+ }
} else if (
Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
context.applicationInfo.targetSdkVersion > Build.VERSION_CODES.UPSIDE_DOWN_CAKE
) {
return DEVICE_TYPE_ANDROID
- } else {
- bluetoothMode =
- Settings.Global.getInt(
- context.contentResolver,
- PAIRED_DEVICE_OS_TYPE,
- UNKNOWN_MODE
- )
}
- return when (bluetoothMode) {
+ pairedDeviceType =
+ Settings.Global.getInt(context.contentResolver, PAIRED_DEVICE_OS_TYPE, UNKNOWN_MODE)
+ return when (pairedDeviceType) {
ANDROID_MODE -> DEVICE_TYPE_ANDROID
IOS_MODE -> DEVICE_TYPE_IOS
NONE_PAIRED_MODE -> DEVICE_TYPE_NONE
diff --git a/webkit/webkit/api/current.txt b/webkit/webkit/api/current.txt
index 23ee69f..4a0366f 100644
--- a/webkit/webkit/api/current.txt
+++ b/webkit/webkit/api/current.txt
@@ -422,8 +422,8 @@
method @AnyThread @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") @UiThread 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") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
- method @SuppressCompatibility @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrlAsync(android.webkit.WebView, String, androidx.core.os.CancellationSignal?, androidx.webkit.PrerenderOperationCallback);
- method @SuppressCompatibility @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrlAsync(android.webkit.WebView, String, androidx.core.os.CancellationSignal?, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.DEFAULT_TRAFFICSTATS_TAGGING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDefaultTrafficStatsTag(int);
@@ -478,7 +478,7 @@
field public static final String MUTE_AUDIO = "MUTE_AUDIO";
field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
- field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL";
+ field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL_V2";
field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String PROFILE_URL_PREFETCH = "PREFETCH_URL_V3";
field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
diff --git a/webkit/webkit/api/restricted_current.txt b/webkit/webkit/api/restricted_current.txt
index 23ee69f..4a0366f 100644
--- a/webkit/webkit/api/restricted_current.txt
+++ b/webkit/webkit/api/restricted_current.txt
@@ -422,8 +422,8 @@
method @AnyThread @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") @UiThread 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") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
- method @SuppressCompatibility @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrlAsync(android.webkit.WebView, String, androidx.core.os.CancellationSignal?, androidx.webkit.PrerenderOperationCallback);
- method @SuppressCompatibility @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrlAsync(android.webkit.WebView, String, androidx.core.os.CancellationSignal?, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.DEFAULT_TRAFFICSTATS_TAGGING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDefaultTrafficStatsTag(int);
@@ -478,7 +478,7 @@
field public static final String MUTE_AUDIO = "MUTE_AUDIO";
field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
- field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL";
+ field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL_V2";
field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String PROFILE_URL_PREFETCH = "PREFETCH_URL_V3";
field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 0fde2c0..a461177 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.webkit.ValueCallback;
@@ -35,7 +36,6 @@
import androidx.annotation.RequiresOptIn;
import androidx.annotation.RestrictTo;
import androidx.annotation.UiThread;
-import androidx.core.os.CancellationSignal;
import androidx.webkit.internal.ApiFeature;
import androidx.webkit.internal.ApiHelperForM;
import androidx.webkit.internal.ApiHelperForO;
@@ -1341,8 +1341,7 @@
}
/**
- * Starts a URL prerender request for this WebView. Can be called from any
- * thread.
+ * Starts a URL prerender request for this WebView. Must be called from the UI thread.
* <p>
* This WebView will use a URL request matching algorithm during execution
* of all variants of {@link android.webkit.WebView#loadUrl(String)} for
@@ -1363,26 +1362,26 @@
* <p>
* The {@link CancellationSignal} will make the best effort to cancel an
* in-flight prerender request; however cancellation it is not guaranteed.
- * <p>
- * All result callbacks will be resolved on the calling thread.
*
* @param webView the WebView for which we trigger the prerender request.
* @param url the url associated with the prerender request.
* @param cancellationSignal used to trigger prerender cancellation.
+ * @param callbackExecutor the executor to resolve the callback with.
* @param callback callbacks for reporting result back to application.
*/
@RequiresFeature(name = WebViewFeature.PRERENDER_WITH_URL,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- @AnyThread
+ @UiThread
@ExperimentalUrlPrerender
- public static void prerenderUrlAsync(
+ public static void prerenderUrl(
@NonNull WebView webView,
@NonNull String url,
@Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor callbackExecutor,
@NonNull PrerenderOperationCallback callback) {
ApiFeature.NoFramework feature = WebViewFeatureInternal.PRERENDER_WITH_URL;
if (feature.isSupportedByWebView()) {
- getProvider(webView).prerenderUrlAsync(url, cancellationSignal, callback);
+ getProvider(webView).prerenderUrl(url, cancellationSignal, callbackExecutor, callback);
} else {
throw WebViewFeatureInternal.getUnsupportedOperationException();
}
@@ -1390,28 +1389,31 @@
/**
* The same as
- * {@link WebViewCompat#prerenderUrlAsync(WebView, String, CancellationSignal, PrerenderOperationCallback)},
+ * {@link WebViewCompat#prerenderUrl(WebView, String, CancellationSignal, Executor, PrerenderOperationCallback)},
* but allows customizing the request by providing {@link SpeculativeLoadingParameters}.
*
* @param webView the WebView for which we trigger the prerender request.
* @param url the url associated with the prerender request.
* @param cancellationSignal used to trigger prerender cancellation.
+ * @param callbackExecutor the executor to resolve the callback with.
* @param params parameters to customize the prerender request.
* @param callback callbacks for reporting result back to application.
*/
@RequiresFeature(name = WebViewFeature.PRERENDER_WITH_URL,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- @AnyThread
+ @UiThread
@ExperimentalUrlPrerender
- public static void prerenderUrlAsync(
+ public static void prerenderUrl(
@NonNull WebView webView,
@NonNull String url,
@Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor callbackExecutor,
@NonNull SpeculativeLoadingParameters params,
@NonNull PrerenderOperationCallback callback) {
ApiFeature.NoFramework feature = WebViewFeatureInternal.PRERENDER_WITH_URL;
if (feature.isSupportedByWebView()) {
- getProvider(webView).prerenderUrlAsync(url, cancellationSignal, params, callback);
+ getProvider(webView).prerenderUrl(url, cancellationSignal, callbackExecutor, params,
+ callback);
} else {
throw WebViewFeatureInternal.getUnsupportedOperationException();
}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 18747e3..2411346 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -653,11 +653,11 @@
/**
* Feature for {@link #isFeatureSupported(String)}.
* This feature covers
- * {@link androidx.webkit.WebViewCompat#prerenderUrlAsync(WebView, String,
- * SpeculativeLoadingParameters, CancellationSignal, PrerenderOperationCallback)}}
+ * {@link androidx.webkit.WebViewCompat#prerenderUrl(WebView, String, CancellationSignal,
+ * Executor, SpeculativeLoadingParameters, PrerenderOperationCallback)}}
*/
@WebViewCompat.ExperimentalUrlPrerender
- public static final String PRERENDER_WITH_URL = "PRERENDER_URL";
+ public static final String PRERENDER_WITH_URL = "PRERENDER_URL_V2";
/**
* Feature for {@link #isFeatureSupported(String)}.
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index fd040be..df7473b 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -31,6 +31,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.webkit.OutcomeReceiverCompat;
+import androidx.webkit.PrerenderOperationCallback;
import androidx.webkit.Profile;
import androidx.webkit.ProfileStore;
import androidx.webkit.ProxyConfig;
@@ -689,8 +690,9 @@
/**
* Feature for {@link WebViewFeature#isFeatureSupported(String)}.
- * This feature covers {@link androidx.webkit.WebViewCompat#prerenderUrlAsync(WebView, String,
- * SpeculativeLoadingParameters, CancellationSignal, PrerenderOperationCallback)}}
+ * This feature covers
+ * {@link androidx.webkit.WebViewCompat#prerenderUrl(WebView, String, CancellationSignal, Executor,
+ * SpeculativeLoadingParameters, PrerenderOperationCallback)}}
*/
public static final ApiFeature.NoFramework PRERENDER_WITH_URL =
new ApiFeature.NoFramework(WebViewFeature.PRERENDER_WITH_URL,
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewProviderAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewProviderAdapter.java
index 0bfb95e..44b00b1 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewProviderAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewProviderAdapter.java
@@ -18,11 +18,13 @@
import android.annotation.SuppressLint;
import android.net.Uri;
+import android.os.CancellationSignal;
+import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import androidx.core.os.CancellationSignal;
+import androidx.webkit.PrerenderException;
import androidx.webkit.PrerenderOperationCallback;
import androidx.webkit.Profile;
import androidx.webkit.SpeculativeLoadingParameters;
@@ -194,24 +196,58 @@
/**
* Adapter method for
- * {@link WebViewCompat#prerenderUrlAsync(WebView, String, CancellationSignal,
+ * {@link WebViewCompat#prerenderUrl(WebView, String, CancellationSignal, Executor,
* PrerenderOperationCallback)}.
*/
- public void prerenderUrlAsync(
+ public void prerenderUrl(
@NonNull String url,
@Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor callbackExecutor,
@NonNull PrerenderOperationCallback callback) {
+
+ ValueCallback<Void> activationCallback = (value) -> {
+ // value will always be null.
+ callback.onPrerenderActivated();
+ };
+ ValueCallback<Throwable> errorCallback = (throwable) -> {
+ callback.onError(new PrerenderException("Prerender operation failed", throwable));
+ };
+ mImpl.prerenderUrl(
+ url,
+ cancellationSignal,
+ callbackExecutor,
+ activationCallback,
+ errorCallback);
}
/**
* Adapter method for
- * {@link WebViewCompat#prerenderUrlAsync(WebView, String, CancellationSignal,
+ * {@link WebViewCompat#prerenderUrl(WebView, String, CancellationSignal, Executor,
* SpeculativeLoadingParameters, PrerenderOperationCallback)}.
*/
- public void prerenderUrlAsync(
+ public void prerenderUrl(
@NonNull String url,
@Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor callbackExecutor,
@NonNull SpeculativeLoadingParameters params,
@NonNull PrerenderOperationCallback callback) {
+
+ InvocationHandler paramsBoundaryInterface =
+ BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
+ new SpeculativeLoadingParametersAdapter(params));
+ ValueCallback<Void> activationCallback = (value) -> {
+ // value will always be null.
+ callback.onPrerenderActivated();
+ };
+ ValueCallback<Throwable> errorCallback = (throwable) -> {
+ callback.onError(new PrerenderException("Prerender operation failed", throwable));
+ };
+ mImpl.prerenderUrl(
+ url,
+ cancellationSignal,
+ callbackExecutor,
+ paramsBoundaryInterface,
+ activationCallback,
+ errorCallback);
}
}