Merge "Add project dependency constraint between lifecycle-livedata and lifecycle-livedata-core" into androidx-main
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
index 9d5eced..f99856a 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
@@ -52,10 +52,13 @@
Landroidx/Foo/Bar;
Lfoo/bar/Baz$dollar<Suffix>;
HSPLjava/io/DataOutputStream;->writeByte(I)V+]Ljava/io/OutputStream;missing_types
+ HPLandroidx/startup/AppInitializer;->**(**)**
""".trimIndent()
val startupRules = startupProfile(profile, includeStartupOnly = true)
val expectedRules = """
+ SLandroidx/Foo/Bar;
+ SLfoo/bar/Baz;
SLjava/io/DataOutputStream;
""".trimIndent()
assertEquals(expectedRules, startupRules)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
index 527791a..8b50582 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
@@ -33,7 +33,10 @@
null -> null
else -> {
val (flags, classPrefix, _) = result.destructured
- if (includeStartupOnly && !flags.contains("S")) {
+ // Empty flags are indicative that the class needs to be aggressively preloaded
+ // Therefore the class belongs in the primary dex.
+ val isStartup = flags.isEmpty() || flags.contains("S")
+ if (includeStartupOnly && !isStartup) {
null
} else {
"SL$classPrefix;"
diff --git a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
index 1c4ab30..82ce1a1 100644
--- a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
+++ b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
@@ -1,6 +1,7 @@
package androidx.bluetooth.core
import android.bluetooth.le.ScanFilter as FwkScanFilter
+import android.os.Build
import android.os.ParcelUuid
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -18,6 +19,8 @@
val TEST_SERVICE_DATA = "SERVICE-DATA".toByteArray()
val TEST_MANUFACTURER_ID = 1000
val TEST_MANUFACTURER_DATA = "MANUFACTURER-DATA".toByteArray()
+ val TEST_SERVICE_SOLICITATION_UUID =
+ ParcelUuid.fromString("CCCCCCC0-CCCC-CCCC-CCCC-CCCCCCCCCCCC")
}
@Test
@@ -30,8 +33,9 @@
serviceData = TEST_SERVICE_DATA,
manufacturerId = TEST_MANUFACTURER_ID,
manufacturerData = TEST_MANUFACTURER_DATA,
+ serviceSolicitationUuid = TEST_SERVICE_SOLICITATION_UUID
)
- val fwkScanFilter = scanFilter.fwkInstance
+ val fwkScanFilter = (scanFilter.impl as ScanFilterImpl).fwkInstance
assertThat(fwkScanFilter.deviceName).isEqualTo(TEST_DEVICE_NAME)
assertThat(fwkScanFilter.deviceAddress).isEqualTo(TEST_DEVICE_ADDRESS)
assertThat(fwkScanFilter.serviceUuid).isEqualTo(TEST_SERVICE_UUID)
@@ -39,18 +43,25 @@
assertThat(fwkScanFilter.serviceData).isEqualTo(TEST_SERVICE_DATA)
assertThat(fwkScanFilter.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
assertThat(fwkScanFilter.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+ if (Build.VERSION.SDK_INT > 29) {
+ assertThat(fwkScanFilter.serviceSolicitationUuid)
+ .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
+ }
}
@Test
fun constructorWithFwkInstance_createsScanFilterCorrectly() {
- val fwkScanFilter = FwkScanFilter.Builder()
+ val fwkScanFilterBuilder = FwkScanFilter.Builder()
.setDeviceName(TEST_DEVICE_NAME)
.setDeviceAddress(TEST_DEVICE_ADDRESS)
.setServiceUuid(TEST_SERVICE_UUID)
.setServiceData(TEST_SERVICE_DATA_UUID, TEST_SERVICE_DATA)
.setManufacturerData(TEST_MANUFACTURER_ID, TEST_MANUFACTURER_DATA)
- .build()
- val scanFilter = ScanFilter(fwkScanFilter)
+ if (Build.VERSION.SDK_INT > 29) {
+ fwkScanFilterBuilder.setServiceSolicitationUuid(TEST_SERVICE_SOLICITATION_UUID)
+ }
+
+ val scanFilter = ScanFilter(fwkScanFilterBuilder.build())
assertThat(scanFilter.deviceName).isEqualTo(TEST_DEVICE_NAME)
assertThat(scanFilter.deviceAddress).isEqualTo(TEST_DEVICE_ADDRESS)
@@ -59,6 +70,10 @@
assertThat(scanFilter.serviceData).isEqualTo(TEST_SERVICE_DATA)
assertThat(scanFilter.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
assertThat(scanFilter.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+ if (Build.VERSION.SDK_INT > 29) {
+ assertThat(scanFilter.serviceSolicitationUuid)
+ .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
+ }
}
@Test
@@ -71,6 +86,7 @@
serviceData = TEST_SERVICE_DATA,
manufacturerId = TEST_MANUFACTURER_ID,
manufacturerData = TEST_MANUFACTURER_DATA,
+ serviceSolicitationUuid = TEST_SERVICE_SOLICITATION_UUID
)
val bundle = scanFilter.toBundle()
@@ -82,5 +98,7 @@
assertThat(scanFilterFromBundle.serviceData).isEqualTo(TEST_SERVICE_DATA)
assertThat(scanFilterFromBundle.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
assertThat(scanFilterFromBundle.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+ assertThat(scanFilterFromBundle.serviceSolicitationUuid)
+ .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
}
}
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
index 98fbd7f..d274970 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
@@ -17,27 +17,237 @@
package androidx.bluetooth.core
import android.bluetooth.le.ScanFilter as FwkScanFilter
+import android.os.Build
import android.os.Bundle
import android.os.ParcelUuid
+import androidx.annotation.RequiresApi
import androidx.bluetooth.core.utils.Bundleable
import androidx.bluetooth.core.utils.Utils
/**
* TODO: Copy docs
- * TODO: Implement ScanFilterImplXxx
- * TODO: Implement APIs added in API 29
- * getters: getServiceSolicitationUuid, getServiceSolicitationUuidMask
- * setters: setServiceSolicitationUuid
* TODO: Implement APIs added in API 33
* getters: getAdvertisingData, getAdvertisingDataMask, getAdvertisingDataType
* setters: setAdvertisingDataTypeWithData, setAdvertisingDataType,
* @hide
*/
-class ScanFilter internal constructor(internal val fwkInstance: FwkScanFilter) : Bundleable {
+class ScanFilter internal constructor(
+ internal val impl: ScanFilterInterface
+) : ScanFilterInterface by impl {
+ companion object {
+ val CREATOR: Bundleable.Creator<ScanFilter> =
+ if (Build.VERSION.SDK_INT < 29) {
+ ScanFilterImplBase.CREATOR
+ } else {
+ ScanFilterImplApi29.CREATOR
+ }
+ internal fun createScanFilterImpl(args: ScanFilterArgs): ScanFilterInterface {
+ return if (Build.VERSION.SDK_INT < 29) {
+ ScanFilterImplBase(
+ ScanFilterImplBase.getFwkScanFilterBuilder(args).build(),
+ args.serviceSolicitationUuid,
+ args.serviceSolicitationUuidMask
+ )
+ } else {
+ ScanFilterImplApi29(
+ ScanFilterImplApi29.getFwkScanFilterBuilder(args).build()
+ )
+ }
+ }
+ }
+ internal constructor(fwkScanFilter: FwkScanFilter) : this(
+ if (Build.VERSION.SDK_INT < 29) {
+ ScanFilterImplBase(fwkScanFilter)
+ } else {
+ ScanFilterImplApi29(fwkScanFilter)
+ }
+ )
+
+ constructor(
+ deviceName: String? = null,
+ deviceAddress: String? = null,
+ serviceUuid: ParcelUuid? = null,
+ serviceUuidMask: ParcelUuid? = null,
+ serviceDataUuid: ParcelUuid? = null,
+ serviceData: ByteArray? = null,
+ serviceDataMask: ByteArray? = null,
+ manufacturerId: Int = -1,
+ manufacturerData: ByteArray? = null,
+ manufacturerDataMask: ByteArray? = null,
+ serviceSolicitationUuid: ParcelUuid? = null,
+ serviceSolicitationUuidMask: ParcelUuid? = null
+ ) : this(createScanFilterImpl(ScanFilterArgs(
+ deviceName,
+ deviceAddress,
+ serviceUuid,
+ serviceUuidMask,
+ serviceDataUuid,
+ serviceData,
+ serviceDataMask,
+ manufacturerId,
+ manufacturerData,
+ manufacturerDataMask,
+ serviceSolicitationUuid,
+ serviceSolicitationUuidMask
+ )))
+}
+
+internal data class ScanFilterArgs(
+ var deviceName: String? = null,
+ var deviceAddress: String? = null,
+ var serviceUuid: ParcelUuid? = null,
+ var serviceUuidMask: ParcelUuid? = null,
+ var serviceDataUuid: ParcelUuid? = null,
+ var serviceData: ByteArray? = null,
+ var serviceDataMask: ByteArray? = null,
+ var manufacturerId: Int = -1,
+ var manufacturerData: ByteArray? = null,
+ var manufacturerDataMask: ByteArray? = null,
+ var serviceSolicitationUuid: ParcelUuid? = null,
+ var serviceSolicitationUuidMask: ParcelUuid? = null
+)
+
+internal interface ScanFilterInterface : Bundleable {
+ val deviceName: String?
+ val deviceAddress: String?
+ val serviceUuid: ParcelUuid?
+ val serviceUuidMask: ParcelUuid?
+ val serviceDataUuid: ParcelUuid?
+ val serviceData: ByteArray?
+ val serviceDataMask: ByteArray?
+ val manufacturerId: Int
+ val manufacturerData: ByteArray?
+ val manufacturerDataMask: ByteArray?
+ val serviceSolicitationUuid: ParcelUuid?
+ val serviceSolicitationUuidMask: ParcelUuid?
+}
+
+internal abstract class ScanFilterImpl internal constructor(
+ internal val fwkInstance: FwkScanFilter
+) : ScanFilterInterface {
companion object {
internal const val FIELD_FWK_SCAN_FILTER = 0
+ internal const val FIELD_SERVICE_SOLICITATION_UUID = 1
+ internal const val FIELD_SERVICE_SOLICITATION_UUID_MASK = 2
+ internal fun keyForField(field: Int): String {
+ return field.toString(Character.MAX_RADIX)
+ }
+ }
+
+ override val deviceName: String?
+ get() = fwkInstance.deviceName
+ override val deviceAddress: String?
+ get() = fwkInstance.deviceAddress
+ override val serviceUuid: ParcelUuid?
+ get() = fwkInstance.serviceUuid
+ override val serviceUuidMask: ParcelUuid?
+ get() = fwkInstance.serviceUuidMask
+ override val serviceDataUuid: ParcelUuid?
+ get() = fwkInstance.serviceDataUuid
+ override val serviceData: ByteArray?
+ get() = fwkInstance.serviceData
+ override val serviceDataMask: ByteArray?
+ get() = fwkInstance.serviceDataMask
+ override val manufacturerId: Int
+ get() = fwkInstance.manufacturerId
+ override val manufacturerData: ByteArray?
+ get() = fwkInstance.manufacturerData
+ override val manufacturerDataMask: ByteArray?
+ get() = fwkInstance.manufacturerDataMask
+}
+
+internal class ScanFilterImplBase internal constructor(
+ fwkInstance: FwkScanFilter,
+ override val serviceSolicitationUuid: ParcelUuid? = null,
+ override val serviceSolicitationUuidMask: ParcelUuid? = null
+) : ScanFilterImpl(fwkInstance) {
+ companion object {
+ val CREATOR: Bundleable.Creator<ScanFilter> =
+ object : Bundleable.Creator<ScanFilter> {
+ override fun fromBundle(bundle: Bundle): ScanFilter {
+ val fwkScanFilter =
+ Utils.getParcelableFromBundle(
+ bundle,
+ keyForField(FIELD_FWK_SCAN_FILTER),
+ FwkScanFilter::class.java
+ ) ?: throw IllegalArgumentException(
+ "Bundle doesn't include a framework scan filter"
+ )
+ val serviceSolicitationUuid =
+ Utils.getParcelableFromBundle(
+ bundle,
+ keyForField(FIELD_SERVICE_SOLICITATION_UUID),
+ ParcelUuid::class.java
+ )
+ val serviceSolicitationUuidMask =
+ Utils.getParcelableFromBundle(
+ bundle,
+ keyForField(FIELD_SERVICE_SOLICITATION_UUID_MASK),
+ ParcelUuid::class.java
+ )
+ return ScanFilter(ScanFilterImplBase(
+ fwkScanFilter,
+ serviceSolicitationUuid,
+ serviceSolicitationUuidMask))
+ }
+ }
+
+ internal fun getFwkScanFilterBuilder(args: ScanFilterArgs): FwkScanFilter.Builder {
+ val builder = FwkScanFilter.Builder()
+ .setDeviceName(args.deviceName)
+ .setDeviceAddress(args.deviceAddress)
+ .setServiceUuid(args.serviceUuid)
+
+ if (args.serviceDataUuid != null) {
+ if (args.serviceDataMask == null) {
+ builder.setServiceData(args.serviceDataUuid, args.serviceData)
+ } else {
+ builder.setServiceData(
+ args.serviceDataUuid, args.serviceData, args.serviceDataMask)
+ }
+ }
+ if (args.manufacturerId >= 0) {
+ if (args.manufacturerDataMask == null) {
+ builder.setManufacturerData(args.manufacturerId, args.manufacturerData)
+ } else {
+ builder.setManufacturerData(
+ args.manufacturerId, args.manufacturerData, args.manufacturerDataMask)
+ }
+ }
+ return builder
+ }
+ }
+
+ override fun toBundle(): Bundle {
+ val bundle = Bundle()
+ bundle.putParcelable(keyForField(FIELD_FWK_SCAN_FILTER), fwkInstance)
+ if (serviceSolicitationUuid != null) {
+ bundle.putParcelable(
+ keyForField(FIELD_SERVICE_SOLICITATION_UUID), serviceSolicitationUuid)
+ }
+ if (serviceSolicitationUuidMask != null) {
+ bundle.putParcelable(
+ keyForField(FIELD_SERVICE_SOLICITATION_UUID_MASK), serviceSolicitationUuidMask)
+ }
+ return bundle
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal abstract class ScanFilterImpl29(fwkInstance: FwkScanFilter) : ScanFilterImpl(fwkInstance) {
+ override val serviceSolicitationUuid: ParcelUuid?
+ get() = fwkInstance.serviceSolicitationUuid
+ override val serviceSolicitationUuidMask: ParcelUuid?
+ get() = fwkInstance.serviceSolicitationUuidMask
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal class ScanFilterImplApi29 internal constructor(
+ fwkInstance: FwkScanFilter
+) : ScanFilterImpl29(fwkInstance) {
+ companion object {
val CREATOR: Bundleable.Creator<ScanFilter> =
object : Bundleable.Creator<ScanFilter> {
override fun fromBundle(bundle: Bundle): ScanFilter {
@@ -53,91 +263,24 @@
}
}
- internal fun keyForField(field: Int): String {
- return field.toString(Character.MAX_RADIX)
- }
+ internal fun getFwkScanFilterBuilder(args: ScanFilterArgs): FwkScanFilter.Builder {
+ val builder = ScanFilterImplBase.getFwkScanFilterBuilder(args)
- internal fun buildFwkScanFilter(
- deviceName: String? = null,
- deviceAddress: String? = null,
- serviceUuid: ParcelUuid? = null,
- serviceDataUuid: ParcelUuid? = null,
- serviceData: ByteArray? = null,
- serviceDataMask: ByteArray? = null,
- manufacturerId: Int = -1,
- manufacturerData: ByteArray? = null,
- manufacturerDataMask: ByteArray? = null
- ): FwkScanFilter {
- var builder = FwkScanFilter.Builder()
- .setDeviceName(deviceName)
- .setDeviceAddress(deviceAddress)
- .setServiceUuid(serviceUuid)
-
- if (serviceDataUuid != null) {
- if (serviceDataMask == null) {
- builder.setServiceData(serviceDataUuid, serviceData)
+ if (args.serviceSolicitationUuid != null) {
+ if (args.serviceSolicitationUuidMask == null) {
+ builder.setServiceSolicitationUuid(args.serviceSolicitationUuid)
} else {
- builder.setServiceData(serviceDataUuid, serviceData, serviceDataMask)
+ builder.setServiceSolicitationUuid(
+ args.serviceSolicitationUuid, args.serviceSolicitationUuidMask)
}
}
- if (manufacturerId >= 0) {
- if (manufacturerDataMask == null) {
- builder.setManufacturerData(manufacturerId, manufacturerData)
- } else {
- builder.setManufacturerData(
- manufacturerId, manufacturerData, manufacturerDataMask)
- }
- }
-
- return builder.build()
+ return builder
}
}
- val deviceName: String?
- get() = fwkInstance.deviceName
- val deviceAddress: String?
- get() = fwkInstance.deviceAddress
- val serviceUuid: ParcelUuid?
- get() = fwkInstance.serviceUuid
- val serviceUuidMask: ParcelUuid?
- get() = fwkInstance.serviceUuidMask
- val serviceDataUuid: ParcelUuid?
- get() = fwkInstance.serviceDataUuid
- val serviceData: ByteArray?
- get() = fwkInstance.serviceData
- val serviceDataMask: ByteArray?
- get() = fwkInstance.serviceDataMask
- val manufacturerId: Int
- get() = fwkInstance.manufacturerId
- val manufacturerData: ByteArray?
- get() = fwkInstance.manufacturerData
- val manufacturerDataMask: ByteArray?
- get() = fwkInstance.manufacturerDataMask
-
- constructor(
- deviceName: String? = null,
- deviceAddress: String? = null,
- serviceUuid: ParcelUuid? = null,
- serviceDataUuid: ParcelUuid? = null,
- serviceData: ByteArray? = null,
- serviceDataMask: ByteArray? = null,
- manufacturerId: Int = -1,
- manufacturerData: ByteArray? = null,
- manufacturerDataMask: ByteArray? = null
- ) : this(buildFwkScanFilter(
- deviceName,
- deviceAddress,
- serviceUuid,
- serviceDataUuid,
- serviceData,
- serviceDataMask,
- manufacturerId,
- manufacturerData,
- manufacturerDataMask
- ))
override fun toBundle(): Bundle {
val bundle = Bundle()
bundle.putParcelable(keyForField(FIELD_FWK_SCAN_FILTER), fwkInstance)
return bundle
}
-}
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
index 1887469..2eb25fc 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
@@ -26,51 +26,48 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
-internal class Utils {
- companion object {
+internal object Utils {
+ // TODO: Migrate to BundleCompat when available
+ @SuppressLint("ClassVerificationFailure") // bundle.getParcelable(key, clazz)
+ @Suppress("DEPRECATION") // bundle.getParcelable(key)
+ fun <T : Parcelable> getParcelableFromBundle(
+ bundle: Bundle,
+ key: String,
+ clazz: Class<T>
+ ): T? {
+ val parcelable: T?
+ bundle.classLoader = clazz.classLoader
+ try {
+ if (Build.VERSION.SDK_INT >= 33) {
+ parcelable = bundle.getParcelable(key, clazz)
+ } else {
+ parcelable = bundle.getParcelable(key)
+ }
+ } catch (e: Exception) {
+ return null
+ }
+ return parcelable
+ }
- // TODO: Migrate to BundleCompat when available
- @SuppressLint("ClassVerificationFailure") // bundle.getParcelable(key, clazz)
- @Suppress("DEPRECATION") // bundle.getParcelable(key)
- fun <T : Parcelable> getParcelableFromBundle(
- bundle: Bundle,
- key: String,
- clazz: Class<T>
- ): T? {
- val parcelable: T?
- bundle.classLoader = clazz.classLoader
+ @Suppress("DEPRECATION")
+ fun <T : Parcelable> getParcelableArrayListFromBundle(
+ bundle: Bundle,
+ key: String,
+ clazz: Class<T>
+ ): List<T> {
+ bundle.classLoader = clazz.classLoader
+ if (Build.VERSION.SDK_INT >= 33) {
+ // TODO: Return framework's getParcelableArrayList when SDK 33 is available
+ // return bundle.getParcelableArrayList(key, clazz)
+ TODO()
+ } else {
+ val parcelable: List<T>
try {
- if (Build.VERSION.SDK_INT >= 33) {
- parcelable = bundle.getParcelable(key, clazz)
- } else {
- parcelable = bundle.getParcelable(key)
- }
+ parcelable = bundle.getParcelableArrayList(key) ?: emptyList()
} catch (e: Exception) {
- return null
+ return emptyList()
}
return parcelable
}
-
- @Suppress("DEPRECATION")
- fun <T : Parcelable> getParcelableArrayListFromBundle(
- bundle: Bundle,
- key: String,
- clazz: Class<T>
- ): List<T> {
- bundle.classLoader = clazz.classLoader
- if (Build.VERSION.SDK_INT >= 33) {
- // TODO: Return framework's getParcelableArrayList when SDK 33 is available
- // return bundle.getParcelableArrayList(key, clazz)
- TODO()
- } else {
- val parcelable: List<T>
- try {
- parcelable = bundle.getParcelableArrayList(key) ?: emptyList()
- } catch (e: Exception) {
- return emptyList()
- }
- return parcelable
- }
- }
}
}
\ No newline at end of file
diff --git a/buildSrc/OWNERS b/buildSrc/OWNERS
index 0aa4786..d675db7 100644
--- a/buildSrc/OWNERS
+++ b/buildSrc/OWNERS
@@ -6,10 +6,5 @@
[email protected]
[email protected]
-per-file *Dependencies.kt=file://OWNERS
-per-file *LibraryVersions.kt=file://OWNERS
-per-file *PublishDocsRules.kt=file://OWNERS
-per-file *build_dependencies.gradle=file://OWNERS
-
per-file *AndroidXPlaygroundRootPlugin.kt = [email protected], [email protected], [email protected]
-per-file *LibraryVersions.kt = [email protected]
+per-file *LintConfiguration.kt = [email protected], [email protected]
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 6b988a0..41cd730 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -165,6 +165,8 @@
// Possible values for this intent key: "switch_test_case", "preview_test_case" or
// "default_test_case".
private static final String INTENT_EXTRA_E2E_TEST_CASE = "e2e_test_case";
+ // Launch the activity with the specified video quality.
+ private static final String INTENT_EXTRA_VIDEO_QUALITY = "video_quality";
public static final String INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation";
// Launch the activity with the specified camera id.
@VisibleForTesting
@@ -1025,6 +1027,31 @@
mAnalysisToggle.setChecked((useCaseCombination & BIND_IMAGE_ANALYSIS) != 0L);
}
+ private void updateVideoQualityByIntent(@NonNull Intent intent) {
+ Bundle bundle = intent.getExtras();
+ if (bundle == null) {
+ return;
+ }
+
+ Quality quality = itemIdToQuality(bundle.getInt(INTENT_EXTRA_VIDEO_QUALITY, 0));
+ if (quality == QUALITY_AUTO || !mVideoToggle.isChecked()) {
+ return;
+ }
+
+ if (mCameraProvider == null) {
+ throw new IllegalStateException("Need to obtain mCameraProvider first!");
+ }
+
+ // Check and set specific quality.
+ Camera targetCamera = mCameraProvider.bindToLifecycle(this, mCurrentCameraSelector);
+ List<Quality> supportedQualities =
+ QualitySelector.getSupportedQualities(targetCamera.getCameraInfo());
+ if (supportedQualities.contains(quality)) {
+ mVideoQuality = quality;
+ mRecordUi.getButtonQuality().setText(getQualityIconName(mVideoQuality));
+ }
+ }
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -1149,6 +1176,7 @@
mInitializationIdlingResource.decrement();
if (cameraProviderResult.hasProvider()) {
mCameraProvider = cameraProviderResult.getProvider();
+ updateVideoQualityByIntent(getIntent());
tryBindUseCases();
} else {
Log.e(TAG, "Failed to retrieve ProcessCameraProvider",
@@ -1221,7 +1249,6 @@
mLaunchingCameraLensFacing = camera2CameraInfo.getCameraCharacteristic(
CameraCharacteristics.LENS_FACING);
}
-
List<UseCase> useCases = buildUseCases();
mCamera = bindToLifecycleSafely(useCases);
diff --git a/compose/foundation/foundation/api/1.3.0-beta01.txt b/compose/foundation/foundation/api/1.3.0-beta01.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/1.3.0-beta01.txt
@@ -722,6 +722,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -722,6 +722,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt b/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
index b54f553..fc23475 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
@@ -353,19 +353,22 @@
package androidx.compose.foundation.gestures.snapping {
public final class LazyListSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.unit.Density,? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+ ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+ method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
}
public final class SnapFlingBehaviorKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
}
@androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
- method public float calculateApproachOffset(float initialVelocity);
- method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds();
- method public float getSnapStepSize();
- property public abstract float snapStepSize;
+ method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
+ method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds(androidx.compose.ui.unit.Density);
+ method public float snapStepSize(androidx.compose.ui.unit.Density);
}
}
@@ -872,6 +875,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index b54f553..fc23475 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -353,19 +353,22 @@
package androidx.compose.foundation.gestures.snapping {
public final class LazyListSnapLayoutInfoProviderKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+ method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.unit.Density,? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+ ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+ method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
}
public final class SnapFlingBehaviorKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
- method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
}
@androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
- method public float calculateApproachOffset(float initialVelocity);
- method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds();
- method public float getSnapStepSize();
- property public abstract float snapStepSize;
+ method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
+ method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds(androidx.compose.ui.unit.Density);
+ method public float snapStepSize(androidx.compose.ui.unit.Density);
}
}
@@ -872,6 +875,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt b/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
@@ -722,6 +722,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -722,6 +722,9 @@
public final class LazyStaggeredGridMeasureKt {
}
+ public final class LazyStaggeredGridMeasurePolicyKt {
+ }
+
}
package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
index 7f172b1..804914a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
@@ -50,6 +50,7 @@
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -83,7 +84,7 @@
private val decayAnimationSpec: DecayAnimationSpec<Float>,
private val lazySnapLayoutInfoProvider: SnapLayoutInfoProvider
) : SnapLayoutInfoProvider by lazySnapLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
+ override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
return decayAnimationSpec.calculateTargetValue(0f, initialVelocity) / 2f
}
}
@@ -110,7 +111,7 @@
private val lazySnapLayoutInfoProvider: SnapLayoutInfoProvider,
private val lazyLayoutState: LazyListState
) : SnapLayoutInfoProvider by lazySnapLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
+ override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
return lazyLayoutState.layoutInfo.visibleItemsInfo.sumOf { it.size }.toFloat()
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
index bc5b486..179cc7d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -19,8 +19,8 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
@@ -39,6 +39,7 @@
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.test.swipeUp
import androidx.compose.ui.test.swipeWithVelocity
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.test.filters.LargeTest
import kotlin.math.abs
@@ -53,24 +54,27 @@
class LazyListSnapFlingBehaviorTest(private val orientation: Orientation) :
BaseLazyListTestWithOrientation(orientation) {
+ private val density: Density
+ get() = rule.density
+
@Test
fun belowThresholdVelocity_lessThanAnItemScroll_shouldStayInSamePage() {
var lazyListState: LazyListState? = null
var stepSize = 0f
var velocityThreshold = 0f
-
// arrange
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState().also { lazyListState = it }
- stepSize = with(LocalDensity.current) { ItemSize.toPx() }
- velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+ stepSize = with(density) { ItemSize.toPx() }
+ velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
MainLayout(state = state)
}
// Scroll a bit
onMainList().swipeOnMainAxis()
rule.waitForIdle()
- val currentItem = getCurrentSnappedItem(lazyListState)
+ val currentItem = density.getCurrentSnappedItem(lazyListState)
// act
onMainList().performTouchInput {
@@ -79,7 +83,7 @@
// assert
rule.runOnIdle {
- val nextItem = getCurrentSnappedItem(lazyListState)
+ val nextItem = density.getCurrentSnappedItem(lazyListState)
assertEquals(currentItem, nextItem)
}
}
@@ -92,16 +96,17 @@
// arrange
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState().also { lazyListState = it }
- stepSize = with(LocalDensity.current) { ItemSize.toPx() }
- velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+ stepSize = with(density) { ItemSize.toPx() }
+ velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
MainLayout(state = state)
}
// Scroll a bit
onMainList().swipeOnMainAxis()
rule.waitForIdle()
- val currentItem = getCurrentSnappedItem(lazyListState)
+ val currentItem = density.getCurrentSnappedItem(lazyListState)
// act
onMainList().performTouchInput {
@@ -113,7 +118,7 @@
// assert
rule.runOnIdle {
- val nextItem = getCurrentSnappedItem(lazyListState)
+ val nextItem = density.getCurrentSnappedItem(lazyListState)
assertEquals(currentItem + 1, nextItem)
}
}
@@ -126,16 +131,17 @@
// arrange
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState().also { lazyListState = it }
- stepSize = with(LocalDensity.current) { ItemSize.toPx() }
- velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+ stepSize = with(density) { ItemSize.toPx() }
+ velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
MainLayout(state = state)
}
// Scroll a bit
onMainList().swipeOnMainAxis()
rule.waitForIdle()
- val currentItem = getCurrentSnappedItem(lazyListState)
+ val currentItem = density.getCurrentSnappedItem(lazyListState)
// act
onMainList().performTouchInput {
@@ -147,7 +153,7 @@
// assert
rule.runOnIdle {
- val nextItem = getCurrentSnappedItem(lazyListState)
+ val nextItem = density.getCurrentSnappedItem(lazyListState)
assertEquals(currentItem + 1, nextItem)
}
}
@@ -160,16 +166,17 @@
// arrange
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState().also { lazyListState = it }
- stepSize = with(LocalDensity.current) { ItemSize.toPx() }
- velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+ stepSize = with(density) { ItemSize.toPx() }
+ velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
MainLayout(state = state)
}
// Scroll a bit
onMainList().swipeOnMainAxis()
rule.waitForIdle()
- val currentItem = getCurrentSnappedItem(lazyListState)
+ val currentItem = density.getCurrentSnappedItem(lazyListState)
// act
onMainList().performTouchInput {
@@ -182,7 +189,7 @@
// assert
rule.runOnIdle {
- val nextItem = getCurrentSnappedItem(lazyListState)
+ val nextItem = density.getCurrentSnappedItem(lazyListState)
assertEquals(currentItem - 1, nextItem)
}
}
@@ -195,16 +202,17 @@
// arrange
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState().also { lazyListState = it }
- stepSize = with(LocalDensity.current) { ItemSize.toPx() }
- velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+ stepSize = with(density) { ItemSize.toPx() }
+ velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
MainLayout(state = state)
}
// Scroll a bit
onMainList().swipeOnMainAxis()
rule.waitForIdle()
- val currentItem = getCurrentSnappedItem(lazyListState)
+ val currentItem = density.getCurrentSnappedItem(lazyListState)
// act
onMainList().performTouchInput {
@@ -216,7 +224,7 @@
// assert
rule.runOnIdle {
- val nextItem = getCurrentSnappedItem(lazyListState)
+ val nextItem = density.getCurrentSnappedItem(lazyListState)
assertEquals(currentItem + 2, nextItem)
}
}
@@ -226,7 +234,6 @@
@Composable
fun MainLayout(state: LazyListState) {
val layoutInfoProvider = remember(state) { SnapLayoutInfoProvider(state) }
-
LazyColumnOrRow(
state = state,
modifier = Modifier.testTag(TestTag),
@@ -248,13 +255,16 @@
}
}
- private fun getCurrentSnappedItem(state: LazyListState?): Int {
+ private fun Density.getCurrentSnappedItem(state: LazyListState?): Int {
var itemIndex = -1
if (state == null) return -1
var minDistance = Float.POSITIVE_INFINITY
(state.layoutInfo.visibleItemsInfo).forEach {
- val distance =
- calculateDistanceToDesiredSnapPosition(state.layoutInfo, it, CenterToCenter)
+ val distance = calculateDistanceToDesiredSnapPosition(
+ state.layoutInfo,
+ it,
+ CenterToCenter
+ )
if (abs(distance) < minDistance) {
minDistance = abs(distance)
itemIndex = it.index
@@ -287,7 +297,7 @@
val ItemSize = 200.dp
const val TestTag = "MainList"
- val CenterToCenter: (Float, Float) -> Float =
+ val CenterToCenter: Density.(Float, Float) -> Float =
{ layoutSize, itemSize -> layoutSize / 2f - itemSize / 2f }
}
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
index 909db89..d889aac 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.filters.LargeTest
@@ -46,17 +47,21 @@
class LazyListSnapLayoutInfoProviderTest(orientation: Orientation) :
BaseLazyListTestWithOrientation(orientation) {
+ private val density: Density
+ get() = rule.density
+
@Test
fun snapStepSize_sameSizeItems_shouldBeAverageItemSize() {
var expectedItemSize = 0f
var actualItemSize = 0f
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState()
val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
- actualItemSize = it.snapStepSize
+ actualItemSize = with(it) { density.snapStepSize() }
}
- expectedItemSize = with(LocalDensity.current) { FixedItemSize.toPx() }
+ expectedItemSize = with(density) { FixedItemSize.toPx() }
MainLayout(
state = state,
layoutInfo = layoutInfoProvider,
@@ -76,9 +81,10 @@
var expectedItemSize = 0f
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState()
val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
- actualItemSize = it.snapStepSize
+ actualItemSize = with(it) { density.snapStepSize() }
}
expectedItemSize = state.layoutInfo.visibleItemsInfo.map { it.size }.average().toFloat()
@@ -95,13 +101,14 @@
var snapStepSize = 0f
var actualItemSize = 0f
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState()
val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
- snapStepSize = it.snapStepSize
+ snapStepSize = with(it) { density.snapStepSize() }
}
- actualItemSize =
- with(LocalDensity.current) { (FixedItemSize + FixedItemSize / 2).toPx() }
+ actualItemSize = with(density) { (FixedItemSize + FixedItemSize / 2).toPx() }
+
MainLayout(
state = state,
layoutInfo = layoutInfoProvider,
@@ -122,9 +129,10 @@
var upperBound = 0f
var lowerBound = 0f
rule.setContent {
+ val density = LocalDensity.current
val state = rememberLazyListState()
val layoutInfoProvider = remember(state) { createLayoutInfo(state) }
- val bounds = layoutInfoProvider.calculateSnappingOffsetBounds()
+ val bounds = with(layoutInfoProvider) { density.calculateSnappingOffsetBounds() }
lowerBound = bounds.start
upperBound = bounds.endInclusive
MainLayout(
@@ -143,12 +151,10 @@
@Test
fun calculateApproachOffset_approachOffsetIsAlwaysZero() {
- var snapLayoutInfoProvider: SnapLayoutInfoProvider? = null
+ lateinit var layoutInfoProvider: SnapLayoutInfoProvider
rule.setContent {
val state = rememberLazyListState()
- val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
- snapLayoutInfoProvider = it
- }
+ layoutInfoProvider = remember(state) { createLayoutInfo(state) }
LazyColumnOrRow(
state = state,
flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
@@ -160,8 +166,8 @@
}
rule.runOnIdle {
- assertEquals(snapLayoutInfoProvider?.calculateApproachOffset(1000f), 0f)
- assertEquals(snapLayoutInfoProvider?.calculateApproachOffset(-1000f), 0f)
+ assertEquals(with(layoutInfoProvider) { density.calculateApproachOffset(1000f) }, 0f)
+ assertEquals(with(layoutInfoProvider) { density.calculateApproachOffset(-1000f) }, 0f)
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
index 4931030..b3d3b3d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
@@ -38,6 +38,7 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import kotlin.test.assertEquals
@@ -54,6 +55,9 @@
private val inspectSpringAnimationSpec = InspectSpringAnimationSpec(spring())
+ private val density: Density
+ get() = rule.density
+
@Test
fun performFling_whenVelocityIsBelowThreshold_shouldShortSnap() {
val testLayoutInfoProvider = TestLayoutInfoProvider()
@@ -105,15 +109,15 @@
@Test
fun findClosestOffset_noFlingDirection_shouldReturnAbsoluteDistance() {
val testLayoutInfoProvider = TestLayoutInfoProvider()
- val offset = findClosestOffset(0f, testLayoutInfoProvider)
+ val offset = findClosestOffset(0f, testLayoutInfoProvider, density)
assertEquals(offset, MinOffset)
}
@Test
fun findClosestOffset_flingDirection_shouldReturnCorrectBound() {
val testLayoutInfoProvider = TestLayoutInfoProvider()
- val forwardOffset = findClosestOffset(1f, testLayoutInfoProvider)
- val backwardOffset = findClosestOffset(-1f, testLayoutInfoProvider)
+ val forwardOffset = findClosestOffset(1f, testLayoutInfoProvider, density)
+ val backwardOffset = findClosestOffset(-1f, testLayoutInfoProvider, density)
assertEquals(forwardOffset, MaxOffset)
assertEquals(backwardOffset, MinOffset)
}
@@ -266,14 +270,15 @@
) : SnapLayoutInfoProvider {
var calculateApproachOffsetCount = 0
- override val snapStepSize: Float
- get() = snapStep
+ override fun Density.snapStepSize(): Float {
+ return snapStep
+ }
- override fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
+ override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
return minOffset.rangeTo(maxOffset)
}
- override fun calculateApproachOffset(initialVelocity: Float): Float {
+ override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
calculateApproachOffsetCount++
return approachOffset
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
new file mode 100644
index 0000000..fdfbecc
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Remeasurement
+import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridPrefetcherTest(
+ orientation: Orientation
+) : BaseLazyStaggeredGridWithOrientation(orientation) {
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun initParameters(): Array<Any> = arrayOf(
+ Orientation.Vertical,
+ Orientation.Horizontal,
+ )
+ }
+
+ val itemsSizePx = 30
+ val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
+
+ internal lateinit var state: LazyStaggeredGridState
+
+ @Test
+ fun notPrefetchingForwardInitially() {
+ composeStaggeredGrid()
+
+ rule.onNodeWithTag("4")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun notPrefetchingBackwardInitially() {
+ composeStaggeredGrid(firstItem = 4)
+
+ rule.onNodeWithTag("0")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingForwardAfterSmallScroll() {
+ composeStaggeredGrid()
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(5f)
+ }
+ }
+
+ waitForPrefetch(4)
+
+ rule.onNodeWithTag("4")
+ .assertExists()
+ rule.onNodeWithTag("5")
+ .assertExists()
+ rule.onNodeWithTag("6")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingBackwardAfterSmallScroll() {
+ composeStaggeredGrid(firstItem = 4, itemOffset = 10)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(-5f)
+ }
+ }
+
+ waitForPrefetch(2)
+
+ rule.onNodeWithTag("2")
+ .assertExists()
+ rule.onNodeWithTag("3")
+ .assertExists()
+ rule.onNodeWithTag("0")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingForwardAndBackward() {
+ composeStaggeredGrid(firstItem = 4)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(5f)
+ }
+ }
+
+ waitForPrefetch(8)
+
+ rule.onNodeWithTag("8")
+ .assertExists()
+ rule.onNodeWithTag("9")
+ .assertExists()
+ rule.onNodeWithTag("0")
+ .assertDoesNotExist()
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(-5f)
+ }
+ }
+
+ waitForPrefetch(0)
+
+ rule.onNodeWithTag("0")
+ .assertExists()
+ rule.onNodeWithTag("1")
+ .assertExists()
+ rule.onNodeWithTag("8")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingForwardTwice() {
+ composeStaggeredGrid()
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(5f)
+ }
+ }
+
+ waitForPrefetch(4)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(itemsSizePx / 2f)
+ state.scrollBy(itemsSizePx / 2f)
+ }
+ }
+
+ waitForPrefetch(6)
+
+ rule.onNodeWithTag("4")
+ .assertIsDisplayed()
+ rule.onNodeWithTag("6")
+ .assertExists()
+ rule.onNodeWithTag("8")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingBackwardTwice() {
+ composeStaggeredGrid(firstItem = 8)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(-5f)
+ }
+ }
+
+ waitForPrefetch(4)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(-itemsSizePx / 2f)
+ state.scrollBy(-itemsSizePx / 2f)
+ }
+ }
+
+ waitForPrefetch(2)
+
+ rule.onNodeWithTag("4")
+ .assertIsDisplayed()
+ rule.onNodeWithTag("6")
+ .assertIsDisplayed()
+ rule.onNodeWithTag("2")
+ .assertExists()
+ rule.onNodeWithTag("0")
+ .assertDoesNotExist()
+ }
+
+ @Test
+ fun prefetchingCancelledIfScrollDirectionChanged() {
+ composeStaggeredGrid(firstItem = 8)
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollBy(-5f)
+ state.scrollBy(5f)
+ }
+ }
+
+ waitForPrefetch(12)
+
+ rule.onNodeWithTag("12")
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNodeWithTag("13")
+ .assertExists()
+ .assertIsNotDisplayed()
+ rule.onNodeWithTag("4")
+ .assertDoesNotExist()
+ rule.onNodeWithTag("5")
+ .assertDoesNotExist()
+ }
+
+// @Test
+// fun prefetchingForwardAndBackwardReverseLayout() {
+// composeStaggeredGrid(firstItem = 2, reverseLayout = true)
+//
+// rule.runOnIdle {
+// runBlocking {
+// state.scrollBy(5f)
+// }
+// }
+//
+// waitForPrefetch(6)
+//
+// rule.onNodeWithTag("6")
+// .assertExists()
+// rule.onNodeWithTag("7")
+// .assertExists()
+// rule.onNodeWithTag("0")
+// .assertDoesNotExist()
+// rule.onNodeWithTag("1")
+// .assertDoesNotExist()
+//
+// rule.runOnIdle {
+// runBlocking {
+// state.scrollBy(-2f)
+// state.scrollBy(-1f)
+// }
+// }
+//
+// waitForPrefetch(0)
+//
+// rule.onNodeWithTag("0")
+// .assertExists()
+// rule.onNodeWithTag("1")
+// .assertExists()
+// rule.onNodeWithTag("6")
+// .assertDoesNotExist()
+// rule.onNodeWithTag("7")
+// .assertDoesNotExist()
+// }
+
+// @Test
+// fun prefetchingForwardAndBackwardWithContentPadding() {
+// val halfItemSize = itemsSizeDp / 2f
+// composeStaggeredGrid(
+// firstItem = 4,
+// itemOffset = 5,
+// contentPadding = PaddingValues(mainAxis = halfItemSize)
+// )
+//
+// rule.onNodeWithTag("2")
+// .assertIsDisplayed()
+// rule.onNodeWithTag("4")
+// .assertIsDisplayed()
+// rule.onNodeWithTag("6")
+// .assertIsDisplayed()
+// rule.onNodeWithTag("0")
+// .assertDoesNotExist()
+// rule.onNodeWithTag("8")
+// .assertDoesNotExist()
+//
+// rule.runOnIdle {
+// runBlocking {
+// state.scrollBy(5f)
+// }
+// }
+//
+// waitForPrefetch(6)
+//
+// rule.onNodeWithTag("8")
+// .assertExists()
+// rule.onNodeWithTag("0")
+// .assertDoesNotExist()
+//
+// rule.runOnIdle {
+// runBlocking {
+// state.scrollBy(-2f)
+// }
+// }
+//
+// waitForPrefetch(0)
+//
+// rule.onNodeWithTag("0")
+// .assertExists()
+// }
+
+ @Test
+ fun disposingWhilePrefetchingScheduled() {
+ var emit = true
+ lateinit var remeasure: Remeasurement
+ state = LazyStaggeredGridState()
+ rule.setContent {
+ SubcomposeLayout(
+ modifier = object : RemeasurementModifier {
+ override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
+ remeasure = remeasurement
+ }
+ }
+ ) { constraints ->
+ val placeable = if (emit) {
+ subcompose(Unit) {
+ LazyStaggeredGrid(
+ 2,
+ Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+ state,
+ ) {
+ items(1000) {
+ Spacer(
+ Modifier.mainAxisSize(itemsSizeDp)
+ )
+ }
+ }
+ }.first().measure(constraints)
+ } else {
+ null
+ }
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ placeable?.place(0, 0)
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ // this will schedule the prefetching
+ runBlocking(AutoTestFrameClock()) {
+ state.scrollBy(itemsSizePx.toFloat())
+ }
+ // then we synchronously dispose LazyColumn
+ emit = false
+ remeasure.forceRemeasure()
+ }
+
+ rule.runOnIdle { }
+ }
+
+// @Test
+// fun snappingToOtherPositionWhilePrefetchIsScheduled() {
+// val composedItems = mutableListOf<Int>()
+// rule.setContent {
+// state = rememberLazyGridState()
+// LazyGrid(
+// 1,
+// Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+// state,
+// ) {
+// items(1000) {
+// composedItems.add(it)
+// Spacer(Modifier.mainAxisSize(itemsSizeDp))
+// }
+// }
+// }
+//
+// rule.runOnIdle {
+// // now we have items 0 and 1 visible
+// runBlocking(AutoTestFrameClock()) {
+// // this will move the viewport so items 1 and 2 are visible
+// // and schedule a prefetching for 3
+// state.scrollBy(itemsSizePx.toFloat())
+// // then we move so that items 100 and 101 are visible.
+// // this should cancel the prefetch for 3
+// state.scrollToItem(100)
+// }
+// }
+//
+// // wait a few frames to make sure prefetch happens if was scheduled
+// rule.waitForIdle()
+// rule.waitForIdle()
+// rule.waitForIdle()
+//
+// rule.runOnIdle {
+// Truth.assertThat(composedItems).doesNotContain(3)
+// }
+// }
+
+ private fun waitForPrefetch(index: Int) {
+ rule.waitUntil {
+ activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ }
+ }
+
+ private val activeNodes = mutableSetOf<Int>()
+ private val activeMeasuredNodes = mutableSetOf<Int>()
+
+ private fun composeStaggeredGrid(
+ firstItem: Int = 0,
+ itemOffset: Int = 0,
+ ) {
+ state = LazyStaggeredGridState(
+ initialFirstVisibleItems = intArrayOf(firstItem, firstItem + 1),
+ initialFirstVisibleOffsets = intArrayOf(itemOffset, itemOffset)
+ )
+ rule.setContent {
+ LazyStaggeredGrid(
+ 2,
+ Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+ state,
+ ) {
+ items(100) {
+ DisposableEffect(it) {
+ activeNodes.add(it)
+ onDispose {
+ activeNodes.remove(it)
+ activeMeasuredNodes.remove(it)
+ }
+ }
+ Spacer(
+ Modifier
+ .mainAxisSize(itemsSizeDp)
+ .border(Dp.Hairline, Color.Black)
+ .testTag("$it")
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ activeMeasuredNodes.add(it)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt
new file mode 100644
index 0000000..63621ea
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import kotlin.test.assertEquals
+import org.junit.Test
+
+class LazyStaggeredGridSpansTest {
+ private val spans = LazyStaggeredGridSpans()
+
+ @Test
+ fun emptySpan_unset() {
+ assertEquals(LazyStaggeredGridSpans.Unset, spans.getSpan(0))
+ }
+
+ @Test
+ fun setSpan() {
+ spans.setSpan(0, 42)
+ spans.setSpan(1, 0)
+
+ assertEquals(42, spans.getSpan(0))
+ assertEquals(0, spans.getSpan(1))
+ }
+
+ @Test
+ fun setSpan_beyondBound() {
+ val bound = spans.upperBound()
+ spans.setSpan(bound - 1, 42)
+ spans.setSpan(bound, 42)
+
+ assertEquals(42, spans.getSpan(bound - 1))
+ assertEquals(42, spans.getSpan(bound))
+ }
+
+ @Test
+ fun setSpan_largeNumber() {
+ spans.setSpan(Int.MAX_VALUE / 2, 42)
+
+ assertEquals(42, spans.getSpan(Int.MAX_VALUE / 2))
+ }
+
+ @Test
+ fun setSpan_decreaseBound() {
+ spans.setSpan(Int.MAX_VALUE / 2, 42)
+ spans.setSpan(0, 42)
+
+ assertEquals(-1, spans.getSpan(Int.MAX_VALUE / 2))
+ assertEquals(42, spans.getSpan(0))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun setSpan_negative() {
+ spans.setSpan(-1, 0)
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
index df3d0f3..358ed79 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
@@ -17,11 +17,19 @@
package androidx.compose.foundation.lazy.staggeredgrid
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.scrollBy
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
@@ -284,6 +292,418 @@
}
@Test
+ fun itemsAreCorrectedWhenSizeIncreased() {
+ val state = LazyStaggeredGridState()
+ var expanded by mutableStateOf(false)
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp * 2
+ ),
+ ) {
+ item {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = if (expanded) itemSizeDp * 2 else itemSizeDp
+ ).testTag("$it")
+ )
+ }
+ items(5) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp
+ ).testTag("${it + 1}")
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("1")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+
+ state.scrollBy(itemSizeDp * 3)
+
+ expanded = true
+
+ state.scrollBy(-itemSizeDp * 3)
+
+ rule.onNodeWithTag("0")
+ .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ }
+
+ @Test
+ fun itemsAreCorrectedWhenSizeDecreased() {
+ val state = LazyStaggeredGridState()
+ var expanded by mutableStateOf(true)
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp * 2
+ ),
+ ) {
+ item {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = if (expanded) itemSizeDp * 2 else itemSizeDp
+ ).testTag("$it")
+ )
+ }
+ items(5) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp
+ ).testTag("${it + 1}")
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("1")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+
+ state.scrollBy(itemSizeDp * 3)
+
+ expanded = false
+
+ state.scrollBy(-itemSizeDp * 3)
+
+ rule.onNodeWithTag("0")
+ .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ }
+
+ @Test
+ fun itemsAreCorrectedWithWrongColumns() {
+ val state = LazyStaggeredGridState(
+ // intentionally wrong values, normally items should be [0, 1][2, 3][4, 5]
+ initialFirstVisibleItems = intArrayOf(3, 4),
+ initialFirstVisibleOffsets = intArrayOf(itemSizePx / 2, itemSizePx / 2)
+ )
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp
+ ),
+ ) {
+ items(6) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp
+ ).testTag("$it")
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertDoesNotExist()
+
+ rule.onNodeWithTag("3")
+ .assertMainAxisStartPositionInRootIsEqualTo(-itemSizeDp / 2)
+
+ rule.onNodeWithTag("4")
+ .assertMainAxisSizeIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(-itemSizeDp / 2)
+
+ state.scrollBy(-itemSizeDp * 3)
+
+ rule.onNodeWithTag("0")
+ .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("1")
+ .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun addItems() {
+ val state = LazyStaggeredGridState()
+ var itemsCount by mutableStateOf(1)
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp
+ ),
+ ) {
+ items(itemsCount) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp
+ ).testTag("$it")
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("1")
+ .assertDoesNotExist()
+
+ itemsCount = 10
+
+ rule.waitForIdle()
+
+ state.scrollBy(itemSizeDp * 10)
+
+ rule.onNodeWithTag("8")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("9")
+ .assertIsDisplayed()
+
+ itemsCount = 20
+
+ rule.waitForIdle()
+
+ state.scrollBy(itemSizeDp * 10)
+
+ rule.onNodeWithTag("18")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("19")
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun removeItems() {
+ val state = LazyStaggeredGridState()
+ var itemsCount by mutableStateOf(20)
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp
+ ),
+ ) {
+ items(itemsCount) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp
+ ).testTag("$it")
+ )
+ }
+ }
+ }
+
+ state.scrollBy(itemSizeDp * 20)
+
+ rule.onNodeWithTag("18")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("19")
+ .assertIsDisplayed()
+
+ itemsCount = 10
+
+ rule.onNodeWithTag("8")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("9")
+ .assertIsDisplayed()
+
+ itemsCount = 1
+
+ rule.onNodeWithTag("0")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("1")
+ // seems like reuse keeps the node around?
+ .assertIsNotDisplayed()
+ }
+
+ @Test
+ fun resizingItems_maintainsScrollingRange() {
+ val state = LazyStaggeredGridState()
+ var itemSizes by mutableStateOf(
+ List(20) {
+ itemSizeDp * (it % 4 + 1)
+ }
+ )
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp * 5
+ ).testTag("lazy").border(1.dp, Color.Red),
+ ) {
+ items(20) {
+ Box(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizes[it]
+ ).testTag("$it").border(1.dp, Color.Black)
+ ) {
+ BasicText("$it")
+ }
+ }
+ }
+ }
+
+ rule.onNodeWithTag("lazy")
+ .scrollMainAxisBy(itemSizeDp * 20)
+
+ rule.onNodeWithTag("18")
+ .assertMainAxisSizeIsEqualTo(itemSizes[18])
+
+ rule.onNodeWithTag("19")
+ .assertMainAxisSizeIsEqualTo(itemSizes[19])
+
+ itemSizes = itemSizes.reversed()
+
+ rule.onNodeWithTag("18")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("19")
+ .assertIsDisplayed()
+
+ rule.onNodeWithTag("lazy")
+ .scrollMainAxisBy(-itemSizeDp * 20)
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("1")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun removingItems_maintainsCorrectOffsets() {
+ val state = LazyStaggeredGridState(
+ initialFirstVisibleItems = intArrayOf(10, 11),
+ initialFirstVisibleOffsets = intArrayOf(0, 0)
+ )
+ var itemCount by mutableStateOf(20)
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp * 5
+ ).testTag("lazy").border(1.dp, Color.Red),
+ ) {
+ items(itemCount) {
+ Box(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp * (it % 3 + 1)
+ ).testTag("$it").border(1.dp, Color.Black)
+ ) {
+ BasicText("$it")
+ }
+ }
+ }
+ }
+
+ itemCount = 3
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("1")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+ }
+
+ @Test
+ fun staggeredGrid_supportsLargeIndices() {
+ val state = LazyStaggeredGridState(
+ initialFirstVisibleItems = intArrayOf(Int.MAX_VALUE / 2, Int.MAX_VALUE / 2 + 1),
+ initialFirstVisibleOffsets = intArrayOf(0, 0)
+ )
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ state = state,
+ modifier = Modifier.axisSize(
+ crossAxis = itemSizeDp * 2,
+ mainAxis = itemSizeDp * 5
+ ).testTag("lazy").border(1.dp, Color.Red),
+ ) {
+ items(Int.MAX_VALUE) {
+ Spacer(
+ Modifier.axisSize(
+ crossAxis = itemSizeDp,
+ mainAxis = itemSizeDp * (it % 3 + 1)
+ ).testTag("$it").border(1.dp, Color.Black)
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("${Int.MAX_VALUE / 2}")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ rule.onNodeWithTag("${Int.MAX_VALUE / 2 + 1}")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+ // check that scrolling back and forth doesn't crash
+ rule.onNodeWithTag("lazy")
+ .scrollMainAxisBy(10000.dp)
+
+ rule.onNodeWithTag("lazy")
+ .scrollMainAxisBy(-10000.dp)
+ }
+
+ @Test
fun scrollingALot_layoutIsNotRecomposed() {
val state = LazyStaggeredGridState()
var recomposed = 0
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 084ef75..99deaa7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListLayoutInfo
import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastSumBy
@@ -32,23 +33,22 @@
* This position should be considered with regard to the start edge of the item and the placement
* within the viewport.
*
- * @return A [SnapLayoutInfoProvider] that can be used with [snapFlingBehavior]
+ * @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
*/
@ExperimentalFoundationApi
fun SnapLayoutInfoProvider(
lazyListState: LazyListState,
- positionInLayout: (layoutSize: Float, itemSize: Float) -> Float = { layoutSize, itemSize ->
- layoutSize / 2f - itemSize / 2f
- }
-) = object : SnapLayoutInfoProvider {
+ positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float =
+ { layoutSize, itemSize -> (layoutSize / 2f - itemSize / 2f) }
+): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider {
private val layoutInfo: LazyListLayoutInfo
get() = lazyListState.layoutInfo
// Single page snapping is the default
- override fun calculateApproachOffset(initialVelocity: Float): Float = 0f
+ override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
- override fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
+ override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
var lowerBoundOffset = Float.NEGATIVE_INFINITY
var upperBoundOffset = Float.POSITIVE_INFINITY
@@ -70,20 +70,19 @@
return lowerBoundOffset.rangeTo(upperBoundOffset)
}
- override val snapStepSize: Float
- get() = with(layoutInfo) {
- if (visibleItemsInfo.isNotEmpty()) {
- visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
- } else {
- 0f
- }
+ override fun Density.snapStepSize(): Float = with(layoutInfo) {
+ if (visibleItemsInfo.isNotEmpty()) {
+ visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
+ } else {
+ 0f
}
+ }
}
-internal fun calculateDistanceToDesiredSnapPosition(
+internal fun Density.calculateDistanceToDesiredSnapPosition(
layoutInfo: LazyListLayoutInfo,
item: LazyListItemInfo,
- positionInLayout: (layoutSize: Float, itemSize: Float) -> Float
+ positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float
): Float {
val containerSize =
with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
index ffae738..3bdcfcb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
@@ -73,14 +73,15 @@
*
*/
@ExperimentalFoundationApi
-fun snapFlingBehavior(
- snapLayoutInfoProvider: SnapLayoutInfoProvider,
- approachAnimationSpec: DecayAnimationSpec<Float>,
- snapAnimationSpec: AnimationSpec<Float>,
- density: Density,
- shortSnapVelocityThreshold: Dp = MinFlingVelocityDp
-) = object : FlingBehavior {
- val velocityThreshold = with(density) { shortSnapVelocityThreshold.toPx() }
+class SnapFlingBehavior(
+ private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
+ private val approachAnimationSpec: DecayAnimationSpec<Float>,
+ private val snapAnimationSpec: AnimationSpec<Float>,
+ private val density: Density,
+ private val shortSnapVelocityThreshold: Dp = MinFlingVelocityDp
+) : FlingBehavior {
+
+ private val velocityThreshold = with(density) { shortSnapVelocityThreshold.toPx() }
override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
// If snapping from scroll (short snap) or fling (long snap)
@@ -93,15 +94,16 @@
}
private suspend fun ScrollScope.shortSnap(velocity: Float) {
- val closestOffset = findClosestOffset(0f, snapLayoutInfoProvider)
+ val closestOffset = findClosestOffset(0f, snapLayoutInfoProvider, density)
val animationState = AnimationState(NoDistance, velocity)
animateSnap(closestOffset, closestOffset, animationState, snapAnimationSpec)
}
private suspend fun ScrollScope.longSnap(initialVelocity: Float) {
- val initialOffset = snapLayoutInfoProvider.calculateApproachOffset(initialVelocity).let {
- abs(it) * sign(initialVelocity) // ensure offset sign is correct
- }
+ val initialOffset =
+ with(snapLayoutInfoProvider) { density.calculateApproachOffset(initialVelocity) }.let {
+ abs(it) * sign(initialVelocity) // ensure offset sign is correct
+ }
val (remainingOffset, animationState) = runApproach(initialOffset, initialVelocity)
@@ -115,12 +117,18 @@
val animation =
if (isDecayApproachPossible(offset = initialTargetOffset, velocity = initialVelocity)) {
- DecayApproachAnimation(approachAnimationSpec, snapLayoutInfoProvider)
+ DecayApproachAnimation(approachAnimationSpec, snapLayoutInfoProvider, density)
} else {
- SnapApproachAnimation(snapAnimationSpec, snapLayoutInfoProvider)
+ SnapApproachAnimation(snapAnimationSpec, snapLayoutInfoProvider, density)
}
- return approach(initialTargetOffset, initialVelocity, animation, snapLayoutInfoProvider)
+ return approach(
+ initialTargetOffset,
+ initialVelocity,
+ animation,
+ snapLayoutInfoProvider,
+ density
+ )
}
/**
@@ -131,9 +139,28 @@
velocity: Float
): Boolean {
val decayOffset = approachAnimationSpec.calculateTargetValue(NoDistance, velocity)
- val stepSize = snapLayoutInfoProvider.snapStepSize
+ val stepSize = with(snapLayoutInfoProvider) { density.snapStepSize() }
return abs(decayOffset) > stepSize && abs(decayOffset) - stepSize > abs(offset)
}
+
+ override fun equals(other: Any?): Boolean {
+ return if (other is SnapFlingBehavior) {
+ other.snapAnimationSpec == this.snapAnimationSpec &&
+ other.approachAnimationSpec == this.approachAnimationSpec &&
+ other.snapLayoutInfoProvider == this.snapLayoutInfoProvider &&
+ other.density == this.density &&
+ other.shortSnapVelocityThreshold == this.shortSnapVelocityThreshold
+ } else {
+ false
+ }
+ }
+
+ override fun hashCode(): Int = 0
+ .let { 31 * it + snapAnimationSpec.hashCode() }
+ .let { 31 * it + approachAnimationSpec.hashCode() }
+ .let { 31 * it + snapLayoutInfoProvider.hashCode() }
+ .let { 31 * it + density.hashCode() }
+ .let { 31 * it + shortSnapVelocityThreshold.hashCode() }
}
/**
@@ -148,20 +175,18 @@
snapLayoutInfoProvider: SnapLayoutInfoProvider,
approachAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow)
-): FlingBehavior {
-
- val currentDensity = LocalDensity.current
+): SnapFlingBehavior {
+ val density = LocalDensity.current
return remember(
snapLayoutInfoProvider,
- currentDensity,
approachAnimationSpec,
snapAnimationSpec
) {
- snapFlingBehavior(
+ SnapFlingBehavior(
snapLayoutInfoProvider,
approachAnimationSpec,
snapAnimationSpec,
- currentDensity
+ density
)
}
}
@@ -183,16 +208,17 @@
initialTargetOffset: Float,
initialVelocity: Float,
animation: ApproachAnimation<Float, AnimationVector1D>,
- snapLayoutInfoProvider: SnapLayoutInfoProvider
+ snapLayoutInfoProvider: SnapLayoutInfoProvider,
+ density: Density
): ApproachStepResult {
var currentAnimationState =
animation.approachAnimation(this, initialTargetOffset, initialVelocity)
var remainingOffset =
- findClosestOffset(currentAnimationState.velocity, snapLayoutInfoProvider)
+ findClosestOffset(currentAnimationState.velocity, snapLayoutInfoProvider, density)
- val currentHalfStep = snapLayoutInfoProvider.halfStep
+ val currentHalfStep = snapLayoutInfoProvider.halfStep(density)
if (abs(remainingOffset) > currentHalfStep) {
currentAnimationState =
animation.halfStepAnimation(this, remainingOffset, currentAnimationState)
@@ -221,13 +247,16 @@
@OptIn(ExperimentalFoundationApi::class)
internal fun findClosestOffset(
velocity: Float,
- snapLayoutInfoProvider: SnapLayoutInfoProvider
+ snapLayoutInfoProvider: SnapLayoutInfoProvider,
+ density: Density
): Float {
fun Float.isValidDistance(): Boolean {
return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
}
- val (lowerBound, upperBound) = snapLayoutInfoProvider.calculateSnappingOffsetBounds()
+ val (lowerBound, upperBound) = with(snapLayoutInfoProvider) {
+ density.calculateSnappingOffsetBounds()
+ }
val finalDistance = when (sign(velocity)) {
0f -> {
@@ -324,8 +353,9 @@
}
@OptIn(ExperimentalFoundationApi::class)
-private val SnapLayoutInfoProvider.halfStep
- get() = snapStepSize / 2f
+private fun SnapLayoutInfoProvider.halfStep(density: Density): Float {
+ return density.snapStepSize() / 2f
+}
/**
* The animations used to approach offset and approach half a step offset.
@@ -347,7 +377,8 @@
@OptIn(ExperimentalFoundationApi::class)
private class SnapApproachAnimation(
private val snapAnimationSpec: AnimationSpec<Float>,
- private val snapLayoutInfoProvider: SnapLayoutInfoProvider
+ private val layoutInfoProvider: SnapLayoutInfoProvider,
+ private val density: Density
) : ApproachAnimation<Float, AnimationVector1D> {
override suspend fun approachAnimation(
scope: ScrollScope,
@@ -355,9 +386,11 @@
velocity: Float
): AnimationState<Float, AnimationVector1D> {
val animationState = AnimationState(initialValue = 0f, initialVelocity = velocity)
+ val targetOffset =
+ (abs(offset) + with(layoutInfoProvider) { density.snapStepSize() }) * sign(velocity)
return with(scope) {
animateSnap(
- targetOffset = (abs(offset) + snapLayoutInfoProvider.snapStepSize) * sign(velocity),
+ targetOffset = targetOffset,
cancelOffset = offset,
animationState = animationState,
snapAnimationSpec = snapAnimationSpec,
@@ -375,7 +408,7 @@
return with(scope) {
animateSnap(
targetOffset = offset,
- cancelOffset = snapLayoutInfoProvider.halfStep * sign(animationState.velocity),
+ cancelOffset = layoutInfoProvider.halfStep(density) * sign(animationState.velocity),
animationState = animationState,
snapAnimationSpec = snapAnimationSpec
)
@@ -386,7 +419,8 @@
@OptIn(ExperimentalFoundationApi::class)
private class DecayApproachAnimation(
private val decayAnimationSpec: DecayAnimationSpec<Float>,
- private val snapLayoutInfoProvider: SnapLayoutInfoProvider
+ private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
+ private val density: Density
) : ApproachAnimation<Float, AnimationVector1D> {
override suspend fun approachAnimation(
scope: ScrollScope,
@@ -407,7 +441,7 @@
val animationState = previousAnimationState.copy(value = NoDistance)
return with(scope) {
animateDecay(
- snapLayoutInfoProvider.halfStep * sign(animationState.velocity),
+ snapLayoutInfoProvider.halfStep(density) * sign(animationState.velocity),
animationState,
decayAnimationSpec
)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
index d790179..9b6ca10 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
@@ -17,6 +17,7 @@
package androidx.compose.foundation.gestures.snapping
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.ui.unit.Density
/**
* Provides information about the layout that is using a SnapFlingBehavior.
@@ -30,7 +31,7 @@
/**
* The minimum offset that snapping will use to animate. (e.g. an item size)
*/
- val snapStepSize: Float
+ fun Density.snapStepSize(): Float
/**
* Calculate the distance to navigate before settling into the next snapping bound.
@@ -38,7 +39,7 @@
* @param initialVelocity The current fling movement velocity. You can use this tho calculate a
* velocity based offset.
*/
- fun calculateApproachOffset(initialVelocity: Float): Float
+ fun Density.calculateApproachOffset(initialVelocity: Float): Float
/**
* Given a target placement in a layout, the snapping bounds should be the closest offset we
@@ -48,5 +49,5 @@
*
* Bounds are *always* a negative (lower bound) and a positive (upper bound) value.
*/
- fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float>
+ fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float>
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
index 37dc71a..606aafa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
@@ -21,14 +21,14 @@
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
-import androidx.compose.runtime.remember
/**
* Receiver scope which is used by [LazyColumn] and [LazyRow].
@@ -440,8 +440,7 @@
@ExperimentalFoundationApi
@Composable
fun snapFlingBehavior(lazyListState: LazyListState): FlingBehavior {
- val snappingLayout =
- remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
+ val snappingLayout = remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
return rememberSnapFlingBehavior(snappingLayout)
}
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
index eb8ebd4..67a8e48 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
@@ -38,24 +38,24 @@
internal fun LazyStaggeredGrid(
/** State controlling the scroll position */
state: LazyStaggeredGridState,
+ /** The layout orientation of the grid */
+ orientation: Orientation,
+ /** Prefix sums of cross axis sizes of slots per line, e.g. the columns for vertical grid. */
+ slotSizesSums: Density.(Constraints) -> IntArray,
/** Modifier to be applied for the inner layout */
modifier: Modifier = Modifier,
/** The inner padding to be added for the whole content (not for each individual item) */
contentPadding: PaddingValues = PaddingValues(0.dp),
/** reverse the direction of scrolling and layout */
reverseLayout: Boolean = false,
- /** The layout orientation of the grid */
- orientation: Orientation,
/** fling behavior to be used for flinging */
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
/** Whether scrolling via the user gestures is allowed. */
- userScrollEnabled: Boolean,
+ userScrollEnabled: Boolean = true,
/** The vertical arrangement for items/lines. */
- verticalArrangement: Arrangement.Vertical,
+ verticalArrangement: Arrangement.Vertical = Arrangement.Top,
/** The horizontal arrangement for items/lines. */
- horizontalArrangement: Arrangement.Horizontal,
- /** Prefix sums of cross axis sizes of slots per line, e.g. the columns for vertical grid. */
- slotSizesSums: Density.(Constraints) -> IntArray,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
/** The content of the grid */
content: LazyStaggeredGridScope.() -> Unit
) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 617cbbe..513e3be 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -17,406 +17,478 @@
package androidx.compose.foundation.lazy.staggeredgrid
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.OverscrollEffect
-import androidx.compose.foundation.checkScrollableContainerConstraints
import androidx.compose.foundation.fastFold
import androidx.compose.foundation.fastMaxOfOrNull
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.collection.MutableVector
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.util.fastForEach
import kotlin.math.abs
+import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sign
-@Composable
@ExperimentalFoundationApi
-internal fun rememberStaggeredGridMeasurePolicy(
+internal fun LazyLayoutMeasureScope.measure(
state: LazyStaggeredGridState,
itemProvider: LazyLayoutItemProvider,
- contentPadding: PaddingValues,
- reverseLayout: Boolean,
- orientation: Orientation,
- verticalArrangement: Arrangement.Vertical,
- horizontalArrangement: Arrangement.Horizontal,
- slotSizesSums: Density.(Constraints) -> IntArray,
- overscrollEffect: OverscrollEffect
-): LazyLayoutMeasureScope.(Constraints) -> LazyStaggeredGridMeasureResult = remember(
- state,
- itemProvider,
- contentPadding,
- reverseLayout,
- orientation,
- verticalArrangement,
- horizontalArrangement,
- slotSizesSums,
- overscrollEffect,
-) {
- { constraints ->
- checkScrollableContainerConstraints(
- constraints,
- orientation
- )
- val isVertical = orientation == Orientation.Vertical
+ resolvedSlotSums: IntArray,
+ constraints: Constraints,
+ isVertical: Boolean,
+ beforeContentPadding: Int,
+ afterContentPadding: Int,
+): LazyStaggeredGridMeasureResult {
+ val initialItemIndices: IntArray
+ val initialItemOffsets: IntArray
- val resolvedSlotSums = slotSizesSums(this, constraints)
+ Snapshot.withoutReadObservation {
+ initialItemIndices =
+ if (state.firstVisibleItems.size == resolvedSlotSums.size) {
+ state.firstVisibleItems
+ } else {
+ IntArray(resolvedSlotSums.size) { -1 }
+ }
+ initialItemOffsets =
+ if (state.firstVisibleItemScrollOffsets.size == resolvedSlotSums.size) {
+ state.firstVisibleItemScrollOffsets
+ } else {
+ IntArray(resolvedSlotSums.size) { 0 }
+ }
+ }
+
+ val context = LazyStaggeredGridMeasureContext(
+ state = state,
+ itemProvider = itemProvider,
+ resolvedSlotSums = resolvedSlotSums,
+ constraints = constraints,
+ isVertical = isVertical,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
+ measureScope = this
+ )
+
+ return context.measure(
+ initialScrollDelta = state.scrollToBeConsumed.roundToInt(),
+ initialItemIndices = initialItemIndices,
+ initialItemOffsets = initialItemOffsets,
+ canRestartMeasure = true,
+ )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private class LazyStaggeredGridMeasureContext(
+ val state: LazyStaggeredGridState,
+ val itemProvider: LazyLayoutItemProvider,
+ val resolvedSlotSums: IntArray,
+ val constraints: Constraints,
+ val isVertical: Boolean,
+ val measureScope: LazyLayoutMeasureScope,
+ val beforeContentPadding: Int,
+ val afterContentPadding: Int,
+) {
+ val measuredItemProvider = LazyStaggeredGridMeasureProvider(
+ isVertical,
+ itemProvider,
+ measureScope,
+ resolvedSlotSums
+ ) { index, key, placeables ->
+ LazyStaggeredGridMeasuredItem(
+ index,
+ key,
+ placeables,
+ isVertical
+ )
+ }
+
+ val spans = state.spans
+}
+
+@ExperimentalFoundationApi
+private fun LazyStaggeredGridMeasureContext.measure(
+ initialScrollDelta: Int,
+ initialItemIndices: IntArray,
+ initialItemOffsets: IntArray,
+ canRestartMeasure: Boolean,
+): LazyStaggeredGridMeasureResult {
+ with(measureScope) {
val itemCount = itemProvider.itemCount
- val mainAxisAvailableSize =
- if (isVertical) constraints.maxHeight else constraints.maxWidth
-
- val measuredItemProvider = LazyStaggeredGridMeasureProvider(
- isVertical,
- itemProvider,
- this,
- resolvedSlotSums
- ) { index, key, placeables ->
- LazyStaggeredGridMeasuredItem(
- index,
- key,
- placeables,
- isVertical
- )
- }
-
- val beforeContentPadding = 0
- val afterContentPadding = 0
-
- val initialItemIndices: IntArray
- val initialItemOffsets: IntArray
-
- Snapshot.withoutReadObservation {
- initialItemIndices =
- if (state.firstVisibleItems.size == resolvedSlotSums.size) {
- state.firstVisibleItems
- } else {
- IntArray(resolvedSlotSums.size) { -1 }
- }
- initialItemOffsets =
- if (state.firstVisibleItemScrollOffsets.size == resolvedSlotSums.size) {
- state.firstVisibleItemScrollOffsets
- } else {
- IntArray(resolvedSlotSums.size) { 0 }
- }
- }
-
- val spans = state.spans
- val firstItemIndices = initialItemIndices.copyOf()
- val firstItemOffsets = initialItemOffsets.copyOf()
-
- // Measure items
-
- if (itemCount <= 0) {
- LazyStaggeredGridMeasureResult(
+ if (itemCount <= 0 || resolvedSlotSums.isEmpty()) {
+ return LazyStaggeredGridMeasureResult(
firstVisibleItemIndices = IntArray(0),
firstVisibleItemScrollOffsets = IntArray(0),
consumedScroll = 0f,
measureResult = layout(constraints.minWidth, constraints.minHeight) {},
canScrollForward = false,
canScrollBackward = false,
- visibleItemsInfo = emptyArray()
+ visibleItemsInfo = emptyList(),
+ totalItemsCount = 0
)
- } else {
- // todo(b/182882362): content padding
+ }
- // represents the real amount of scroll we applied as a result of this measure pass.
- var scrollDelta = state.scrollToBeConsumed.roundToInt()
+ // todo(b/182882362): content padding
- // applying the whole requested scroll offset. we will figure out if we can't consume
- // all of it later
- firstItemOffsets.offsetBy(-scrollDelta)
+ val mainAxisAvailableSize =
+ if (isVertical) constraints.maxHeight else constraints.maxWidth
- // if the current scroll offset is less than minimally possible
- if (firstItemIndices[0] == 0 && firstItemOffsets[0] < 0) {
- scrollDelta += firstItemOffsets[0]
- firstItemOffsets.fill(0)
- }
+ // represents the real amount of scroll we applied as a result of this measure pass.
+ var scrollDelta = initialScrollDelta
- // this will contain all the MeasuredItems representing the visible items
- val measuredItems = Array(resolvedSlotSums.size) {
- mutableListOf<LazyStaggeredGridMeasuredItem>()
- }
+ val firstItemIndices = initialItemIndices.copyOf()
+ val firstItemOffsets = initialItemOffsets.copyOf()
- // include the start padding so we compose items in the padding area. before starting
- // scrolling forward we would remove it back
- firstItemOffsets.offsetBy(-beforeContentPadding)
-
- // define min and max offsets (min offset currently includes beforeContentPadding)
- val minOffset = -beforeContentPadding
- val maxOffset = mainAxisAvailableSize
-
- fun hasSpaceOnTop(): Boolean {
- for (column in firstItemIndices.indices) {
- val itemIndex = firstItemIndices[column]
- val itemOffset = firstItemOffsets[column]
-
- if (itemOffset <= 0 && itemIndex > 0) {
- return true
- }
- }
-
- return false
- }
-
- // we had scrolled backward or we compose items in the start padding area, which means
- // items before current firstItemScrollOffset should be visible. compose them and update
- // firstItemScrollOffset
- while (hasSpaceOnTop()) {
- val columnIndex = firstItemOffsets.indexOfMinValue()
- val previousItemIndex = spans.findPreviousItemIndex(
- item = firstItemIndices[columnIndex],
- column = columnIndex
- )
-
- if (previousItemIndex < 0) {
- break
- }
-
- if (spans.getSpan(previousItemIndex) == SpanLookup.SpanUnset) {
- spans.setSpan(previousItemIndex, columnIndex)
- }
-
- val measuredItem = measuredItemProvider.getAndMeasure(
- previousItemIndex,
- columnIndex
- )
- measuredItems[columnIndex].add(0, measuredItem)
-
- firstItemIndices[columnIndex] = previousItemIndex
- firstItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
- }
-
- // if we were scrolled backward, but there were not enough items before. this means
- // not the whole scroll was consumed
- if (firstItemOffsets[0] < minOffset) {
- scrollDelta += firstItemOffsets[0]
- firstItemOffsets.offsetBy(minOffset - firstItemOffsets[0])
- }
-
- val currentItemIndices = initialItemIndices.copyOf()
- val currentItemOffsets = IntArray(initialItemOffsets.size) {
- -(initialItemOffsets[it] - scrollDelta)
- }
-
- // neutralize previously added start padding as we stopped filling the before content padding
- firstItemOffsets.offsetBy(beforeContentPadding)
-
- val maxMainAxis = (maxOffset + afterContentPadding).coerceAtLeast(0)
-
- // compose first visible items we received from state
- currentItemIndices.forEachIndexed { columnIndex, itemIndex ->
- if (itemIndex == -1) return@forEachIndexed
-
- val measuredItem = measuredItemProvider.getAndMeasure(itemIndex, columnIndex)
- currentItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-
- if (
- currentItemOffsets[columnIndex] <= minOffset &&
- measuredItem.index != itemCount - 1
- ) {
- // this item is offscreen and will not be placed. advance item index
- firstItemIndices[columnIndex] = -1
- firstItemOffsets[columnIndex] -= measuredItem.sizeWithSpacings
- } else {
- measuredItems[columnIndex].add(measuredItem)
- }
- }
-
- // then composing visible items forward until we fill the whole viewport.
- // we want to have at least one item in visibleItems even if in fact all the items are
- // offscreen, this can happen if the content padding is larger than the available size.
- while (
- currentItemOffsets.any { it <= maxMainAxis } ||
- measuredItems.all { it.isEmpty() }
- ) {
- val columnIndex = currentItemOffsets.indexOfMinValue()
- val nextItemIndex = spans.findNextItemIndex(
- currentItemIndices[columnIndex],
- columnIndex
- )
-
- if (nextItemIndex == itemCount) {
- break
- }
-
- if (firstItemIndices[columnIndex] == -1) {
- firstItemIndices[columnIndex] = nextItemIndex
- }
- spans.setSpan(nextItemIndex, columnIndex)
-
- val measuredItem = measuredItemProvider.getAndMeasure(nextItemIndex, columnIndex)
- currentItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-
- if (
- currentItemOffsets[columnIndex] <= minOffset &&
- measuredItem.index != itemCount - 1
- ) {
- // this item is offscreen and will not be placed. advance item index
- firstItemIndices[columnIndex] = -1
- firstItemOffsets[columnIndex] -= measuredItem.sizeWithSpacings
- } else {
- measuredItems[columnIndex].add(measuredItem)
- }
-
- currentItemIndices[columnIndex] = nextItemIndex
- }
-
- // we didn't fill the whole viewport with items starting from firstVisibleItemIndex.
- // lets try to scroll back if we have enough items before firstVisibleItemIndex.
- if (currentItemOffsets.all { it < maxOffset }) {
- val maxOffsetColumn = currentItemOffsets.indexOfMaxValue()
- val toScrollBack = maxOffset - currentItemOffsets[maxOffsetColumn]
- firstItemOffsets.offsetBy(-toScrollBack)
- currentItemOffsets.offsetBy(toScrollBack)
- while (
- firstItemOffsets.any { it < beforeContentPadding } &&
- firstItemIndices.all { it != 0 }
- ) {
- val columnIndex = firstItemOffsets.indexOfMinValue()
- val currentIndex =
- if (firstItemIndices[columnIndex] == -1) {
- itemCount
- } else {
- firstItemIndices[columnIndex]
- }
-
- val previousIndex =
- spans.findPreviousItemIndex(currentIndex, columnIndex)
-
- if (previousIndex < 0) {
- break
- }
-
- val measuredItem = measuredItemProvider.getAndMeasure(
- previousIndex,
- columnIndex
- )
- measuredItems[columnIndex].add(0, measuredItem)
- firstItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
- firstItemIndices[columnIndex] = previousIndex
- }
- scrollDelta += toScrollBack
-
- val minOffsetColumn = firstItemOffsets.indexOfMinValue()
- if (firstItemOffsets[minOffsetColumn] < 0) {
- val offsetValue = firstItemOffsets[minOffsetColumn]
- scrollDelta += offsetValue
- currentItemOffsets.offsetBy(offsetValue)
- firstItemOffsets.offsetBy(-offsetValue)
- }
- }
-
- // report the amount of pixels we consumed. scrollDelta can be smaller than
- // scrollToBeConsumed if there were not enough items to fill the offered space or it
- // can be larger if items were resized, or if, for example, we were previously
- // displaying the item 15, but now we have only 10 items in total in the data set.
- val consumedScroll = if (
- state.scrollToBeConsumed.roundToInt().sign == scrollDelta.sign &&
- abs(state.scrollToBeConsumed.roundToInt()) >= abs(scrollDelta)
- ) {
- scrollDelta.toFloat()
- } else {
- state.scrollToBeConsumed
- }
-
- // todo(b/182882362):
- // even if we compose items to fill before content padding we should ignore items fully
- // located there for the state's scroll position calculation (first item + first offset)
-
- // end measure
-
- val layoutWidth = if (isVertical) {
- constraints.maxWidth
- } else {
- constraints.constrainWidth(currentItemOffsets.max())
- }
- val layoutHeight = if (isVertical) {
- constraints.constrainHeight(currentItemOffsets.max())
- } else {
- constraints.maxHeight
- }
-
- // Placement
-
- val itemScrollOffsets = firstItemOffsets.map { -it }
- val positionedItems = Array(measuredItems.size) {
- mutableListOf<LazyStaggeredGridPositionedItem>()
- }
-
- var currentCrossAxis = 0
- measuredItems.forEachIndexed { i, columnItems ->
- var currentMainAxis = itemScrollOffsets[i]
-
- // todo(b/182882362): arrangement/spacing support
-
- columnItems.fastForEach { item ->
- positionedItems[i] += item.position(
- currentMainAxis,
- currentCrossAxis,
- )
- currentMainAxis += item.sizeWithSpacings
- }
- if (columnItems.isNotEmpty()) {
- currentCrossAxis += columnItems[0].crossAxisSize
- }
- }
-
- // End placement
-
- // todo: reverse layout support
- // only scroll backward if the first item is not on screen or fully visible
- val canScrollBackward = !(firstItemIndices[0] == 0 && firstItemOffsets[0] <= 0)
- // only scroll forward if the last item is not on screen or fully visible
- val canScrollForward = currentItemIndices.indexOf(itemCount - 1).let { columnIndex ->
- if (columnIndex == -1) {
- true
- } else {
- (currentItemOffsets[columnIndex] -
- measuredItems[columnIndex].last().sizeWithSpacings) < mainAxisAvailableSize
- }
- }
-
- @Suppress("UNCHECKED_CAST")
- LazyStaggeredGridMeasureResult(
- firstVisibleItemIndices = firstItemIndices,
- firstVisibleItemScrollOffsets = firstItemOffsets,
- consumedScroll = consumedScroll,
- measureResult = layout(layoutWidth, layoutHeight) {
- positionedItems.forEach {
- it.fastForEach { item ->
- item.place(this)
- }
- }
- },
- canScrollForward = canScrollForward,
- canScrollBackward = canScrollBackward,
- visibleItemsInfo = positionedItems as Array<List<LazyStaggeredGridItemInfo>>
- ).also {
- state.applyMeasureResult(it)
- refreshOverscrollInfo(overscrollEffect, it)
+ // record first span assignments for first items in case they haven't been recorded
+ // before
+ firstItemIndices.forEachIndexed { laneIndex, itemIndex ->
+ if (itemIndex != -1) {
+ spans.setSpan(itemIndex, laneIndex)
}
}
- }
-}
-@OptIn(ExperimentalFoundationApi::class)
-private fun refreshOverscrollInfo(
- overscrollEffect: OverscrollEffect,
- result: LazyStaggeredGridMeasureResult
-) {
- overscrollEffect.isEnabled = result.canScrollForward || result.canScrollBackward
+ // update spans in case item count is lower than before
+ ensureIndicesInRange(firstItemIndices, itemCount)
+
+ // applying the whole requested scroll offset. we will figure out if we can't consume
+ // all of it later
+ firstItemOffsets.offsetBy(-scrollDelta)
+
+ // if the current scroll offset is less than minimally possible
+ if (firstItemIndices[0] == 0 && firstItemOffsets[0] < 0) {
+ scrollDelta += firstItemOffsets[0]
+ firstItemOffsets.fill(0)
+ }
+
+ // this will contain all the MeasuredItems representing the visible items
+ val measuredItems = Array(resolvedSlotSums.size) {
+ ArrayDeque<LazyStaggeredGridMeasuredItem>()
+ }
+
+ // include the start padding so we compose items in the padding area. before starting
+ // scrolling forward we would remove it back
+ firstItemOffsets.offsetBy(-beforeContentPadding)
+
+ // define min and max offsets (min offset currently includes beforeContentPadding)
+ val minOffset = -beforeContentPadding
+ val maxOffset = mainAxisAvailableSize
+
+ fun hasSpaceOnTop(): Boolean {
+ for (lane in firstItemIndices.indices) {
+ val itemIndex = firstItemIndices[lane]
+ val itemOffset = firstItemOffsets[lane]
+
+ if (itemOffset <= 0 && itemIndex > 0) {
+ return true
+ }
+ }
+
+ return false
+ }
+
+ fun misalignedTop(laneIndex: Int): Boolean {
+ // If we scrolled past the first item in the lane, we have a point of reference
+ // to re-align items.
+ // Case 1: Item offsets for first item are not aligned
+ val misalignedOffsets = firstItemOffsets.any {
+ it != firstItemOffsets[laneIndex]
+ }
+ // Case 2: Other lanes have more items than the current one
+ val moreItemsInOtherLanes = firstItemIndices.indices.any { lane ->
+ findPreviousItemIndex(firstItemIndices[lane], lane) != -1
+ }
+ // Case 3: the first item is in the wrong lane (it should always be in
+ // the first one)
+ val firstItemInWrongLane = spans.getSpan(0) != 0
+ // If items are not aligned, reset all measurement data we gathered before and
+ // proceed with initial measure
+ return misalignedOffsets || moreItemsInOtherLanes || firstItemInWrongLane
+ }
+
+ // we had scrolled backward or we compose items in the start padding area, which means
+ // items before current firstItemScrollOffset should be visible. compose them and update
+ // firstItemScrollOffset
+ while (hasSpaceOnTop()) {
+ val laneIndex = firstItemOffsets.indexOfMinValue()
+ val previousItemIndex = findPreviousItemIndex(
+ item = firstItemIndices[laneIndex],
+ lane = laneIndex
+ )
+
+ if (previousItemIndex < 0) {
+ if (misalignedTop(laneIndex) && canRestartMeasure) {
+ spans.reset()
+ return measure(
+ initialScrollDelta = scrollDelta,
+ initialItemIndices = IntArray(firstItemIndices.size) { -1 },
+ initialItemOffsets = IntArray(firstItemOffsets.size) {
+ initialItemOffsets[laneIndex]
+ },
+ canRestartMeasure = false
+ )
+ }
+ break
+ }
+
+ if (spans.getSpan(previousItemIndex) == LazyStaggeredGridSpans.Unset) {
+ spans.setSpan(previousItemIndex, laneIndex)
+ }
+
+ val measuredItem = measuredItemProvider.getAndMeasure(
+ previousItemIndex,
+ laneIndex
+ )
+ measuredItems[laneIndex].addFirst(measuredItem)
+
+ firstItemIndices[laneIndex] = previousItemIndex
+ firstItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+ }
+
+ // if we were scrolled backward, but there were not enough items before. this means
+ // not the whole scroll was consumed
+ if (firstItemOffsets[0] < minOffset) {
+ scrollDelta += firstItemOffsets[0]
+ firstItemOffsets.offsetBy(minOffset - firstItemOffsets[0])
+ }
+
+ val currentItemIndices = initialItemIndices.copyOf().apply {
+ // ensure indices match item count, in case it decreased
+ ensureIndicesInRange(this, itemCount)
+ }
+ val currentItemOffsets = IntArray(initialItemOffsets.size) {
+ -(initialItemOffsets[it] - scrollDelta)
+ }
+
+ // neutralize previously added start padding as we stopped filling the before content padding
+ firstItemOffsets.offsetBy(beforeContentPadding)
+
+ val maxMainAxis = (maxOffset + afterContentPadding).coerceAtLeast(0)
+
+ // compose first visible items we received from state
+ currentItemIndices.forEachIndexed { laneIndex, itemIndex ->
+ if (itemIndex < 0) return@forEachIndexed
+
+ val measuredItem = measuredItemProvider.getAndMeasure(itemIndex, laneIndex)
+ currentItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+
+ spans.setSpan(itemIndex, laneIndex)
+
+ if (
+ currentItemOffsets[laneIndex] <= minOffset &&
+ measuredItem.index != itemCount - 1
+ ) {
+ // this item is offscreen and will not be placed. advance item index
+ firstItemIndices[laneIndex] = -1
+ firstItemOffsets[laneIndex] -= measuredItem.sizeWithSpacings
+ } else {
+ measuredItems[laneIndex].addLast(measuredItem)
+ }
+ }
+
+ // then composing visible items forward until we fill the whole viewport.
+ // we want to have at least one item in visibleItems even if in fact all the items are
+ // offscreen, this can happen if the content padding is larger than the available size.
+ while (
+ currentItemOffsets.any { it <= maxMainAxis } || measuredItems.all { it.isEmpty() }
+ ) {
+ val currentLaneIndex = currentItemOffsets.indexOfMinValue()
+ val nextItemIndex =
+ findNextItemIndex(currentItemIndices[currentLaneIndex], currentLaneIndex)
+
+ if (nextItemIndex >= itemCount) {
+ // if any items changed its size, the spans may not behave correctly
+ // there are no more items in this lane, but there could be more in others
+ // recheck if we can add more items and reset spans accordingly
+ var missedItemIndex = Int.MAX_VALUE
+ currentItemIndices.forEachIndexed { laneIndex, i ->
+ if (laneIndex == currentLaneIndex) return@forEachIndexed
+ var itemIndex = findNextItemIndex(i, laneIndex)
+ while (itemIndex < itemCount) {
+ missedItemIndex = minOf(itemIndex, missedItemIndex)
+ spans.setSpan(itemIndex, LazyStaggeredGridSpans.Unset)
+ itemIndex = findNextItemIndex(itemIndex, laneIndex)
+ }
+ }
+ // there's at least one missed item which may fit current lane
+ if (missedItemIndex != Int.MAX_VALUE && canRestartMeasure) {
+ // reset current lane to the missed item index and restart measure
+ initialItemIndices[currentLaneIndex] =
+ min(initialItemIndices[currentLaneIndex], missedItemIndex)
+ return measure(
+ initialScrollDelta = initialScrollDelta,
+ initialItemIndices = initialItemIndices,
+ initialItemOffsets = initialItemOffsets,
+ canRestartMeasure = false
+ )
+ } else {
+ break
+ }
+ }
+
+ if (firstItemIndices[currentLaneIndex] == -1) {
+ firstItemIndices[currentLaneIndex] = nextItemIndex
+ }
+ spans.setSpan(nextItemIndex, currentLaneIndex)
+
+ val measuredItem =
+ measuredItemProvider.getAndMeasure(nextItemIndex, currentLaneIndex)
+ currentItemOffsets[currentLaneIndex] += measuredItem.sizeWithSpacings
+
+ if (
+ currentItemOffsets[currentLaneIndex] <= minOffset &&
+ measuredItem.index != itemCount - 1
+ ) {
+ // this item is offscreen and will not be placed. advance item index
+ firstItemIndices[currentLaneIndex] = -1
+ firstItemOffsets[currentLaneIndex] -= measuredItem.sizeWithSpacings
+ } else {
+ measuredItems[currentLaneIndex].addLast(measuredItem)
+ }
+
+ currentItemIndices[currentLaneIndex] = nextItemIndex
+ }
+
+ // we didn't fill the whole viewport with items starting from firstVisibleItemIndex.
+ // lets try to scroll back if we have enough items before firstVisibleItemIndex.
+ if (currentItemOffsets.all { it < maxOffset }) {
+ val maxOffsetLane = currentItemOffsets.indexOfMaxValue()
+ val toScrollBack = maxOffset - currentItemOffsets[maxOffsetLane]
+ firstItemOffsets.offsetBy(-toScrollBack)
+ currentItemOffsets.offsetBy(toScrollBack)
+ while (
+ firstItemOffsets.any { it < beforeContentPadding }
+ ) {
+ val laneIndex = firstItemOffsets.indexOfMinValue()
+ val currentIndex =
+ if (firstItemIndices[laneIndex] == -1) {
+ itemCount
+ } else {
+ firstItemIndices[laneIndex]
+ }
+
+ val previousIndex =
+ findPreviousItemIndex(currentIndex, laneIndex)
+
+ if (previousIndex < 0) {
+ if (misalignedTop(laneIndex) && canRestartMeasure) {
+ spans.reset()
+ return measure(
+ initialScrollDelta = scrollDelta,
+ initialItemIndices = IntArray(firstItemIndices.size) { -1 },
+ initialItemOffsets = IntArray(firstItemOffsets.size) { 0 },
+ canRestartMeasure = false
+ )
+ }
+ break
+ }
+
+ spans.setSpan(previousIndex, laneIndex)
+
+ val measuredItem = measuredItemProvider.getAndMeasure(
+ previousIndex,
+ laneIndex
+ )
+ measuredItems[laneIndex].addFirst(measuredItem)
+ firstItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+ firstItemIndices[laneIndex] = previousIndex
+ }
+ scrollDelta += toScrollBack
+
+ val minOffsetLane = firstItemOffsets.indexOfMinValue()
+ if (firstItemOffsets[minOffsetLane] < 0) {
+ val offsetValue = firstItemOffsets[minOffsetLane]
+ scrollDelta += offsetValue
+ currentItemOffsets.offsetBy(offsetValue)
+ firstItemOffsets.offsetBy(-offsetValue)
+ }
+ }
+
+ // report the amount of pixels we consumed. scrollDelta can be smaller than
+ // scrollToBeConsumed if there were not enough items to fill the offered space or it
+ // can be larger if items were resized, or if, for example, we were previously
+ // displaying the item 15, but now we have only 10 items in total in the data set.
+ val consumedScroll = if (
+ state.scrollToBeConsumed.roundToInt().sign == scrollDelta.sign &&
+ abs(state.scrollToBeConsumed.roundToInt()) >= abs(scrollDelta)
+ ) {
+ scrollDelta.toFloat()
+ } else {
+ state.scrollToBeConsumed
+ }
+
+ // todo(b/182882362):
+ // even if we compose items to fill before content padding we should ignore items fully
+ // located there for the state's scroll position calculation (first item + first offset)
+
+ // end measure
+
+ val layoutWidth = if (isVertical) {
+ constraints.maxWidth
+ } else {
+ constraints.constrainWidth(currentItemOffsets.max())
+ }
+ val layoutHeight = if (isVertical) {
+ constraints.constrainHeight(currentItemOffsets.max())
+ } else {
+ constraints.maxHeight
+ }
+
+ // Placement
+
+ val itemScrollOffsets = firstItemOffsets.copyOf().transform { -it }
+ val positionedItems = MutableVector<LazyStaggeredGridPositionedItem>(
+ capacity = measuredItems.sumOf { it.size }
+ )
+ while (measuredItems.any { it.isNotEmpty() }) {
+ // find the next item to position
+ val laneIndex = measuredItems.indexOfMinBy {
+ it.firstOrNull()?.index ?: Int.MAX_VALUE
+ }
+ val item = measuredItems[laneIndex].removeFirst()
+
+ // todo(b/182882362): arrangement/spacing support
+ val mainAxisOffset = itemScrollOffsets[laneIndex]
+ val crossAxisOffset = if (laneIndex == 0) 0 else resolvedSlotSums[laneIndex - 1]
+ positionedItems.add(
+ item.position(
+ laneIndex,
+ mainAxisOffset,
+ crossAxisOffset,
+ )
+ )
+ itemScrollOffsets[laneIndex] += item.sizeWithSpacings
+ }
+
+ // todo: reverse layout support
+
+ // End placement
+
+ // only scroll backward if the first item is not on screen or fully visible
+ val canScrollBackward = !(firstItemIndices[0] == 0 && firstItemOffsets[0] <= 0)
+ // only scroll forward if the last item is not on screen or fully visible
+ val canScrollForward = currentItemOffsets.any { it > maxOffset }
+
+ @Suppress("UNCHECKED_CAST")
+ return LazyStaggeredGridMeasureResult(
+ firstVisibleItemIndices = firstItemIndices,
+ firstVisibleItemScrollOffsets = firstItemOffsets,
+ consumedScroll = consumedScroll,
+ measureResult = layout(layoutWidth, layoutHeight) {
+ positionedItems.forEach { item ->
+ item.place(this)
+ }
+ },
+ canScrollForward = canScrollForward,
+ canScrollBackward = canScrollBackward,
+ visibleItemsInfo = positionedItems.asMutableList(),
+ totalItemsCount = itemCount
+ )
+ }
}
private fun IntArray.offsetBy(delta: Int) {
@@ -438,6 +510,20 @@
return result
}
+private inline fun <T> Array<T>.indexOfMinBy(block: (T) -> Int): Int {
+ var result = -1
+ var min = Int.MAX_VALUE
+ for (i in indices) {
+ val value = block(this[i])
+ if (min > value) {
+ min = value
+ result = i
+ }
+ }
+
+ return result
+}
+
private fun IntArray.indexOfMaxValue(): Int {
var result = -1
var max = Int.MIN_VALUE
@@ -451,26 +537,34 @@
return result
}
-private fun SpanLookup.findPreviousItemIndex(item: Int, column: Int): Int {
- for (i in (item - 1) downTo 0) {
- val span = getSpan(i)
- if (span == column || span == SpanLookup.SpanUnset) {
- return i
- }
+private inline fun IntArray.transform(block: (Int) -> Int): IntArray {
+ for (i in indices) {
+ this[i] = block(this[i])
}
- return -1
+ return this
}
-private fun SpanLookup.findNextItemIndex(item: Int, column: Int): Int {
- for (i in (item + 1) until capacity()) {
- val span = getSpan(i)
- if (span == column || span == SpanLookup.SpanUnset) {
- return i
+private fun LazyStaggeredGridMeasureContext.ensureIndicesInRange(
+ indices: IntArray,
+ itemCount: Int
+) {
+ for (i in indices.indices) {
+ while (indices[i] >= itemCount) {
+ indices[i] = findPreviousItemIndex(indices[i], i)
+ }
+ if (indices[i] != LazyStaggeredGridSpans.Unset) {
+ // reserve item for span
+ spans.setSpan(indices[i], i)
}
}
- return capacity()
}
+private fun LazyStaggeredGridMeasureContext.findPreviousItemIndex(item: Int, lane: Int): Int =
+ spans.findPreviousItemIndex(item, lane)
+
+private fun LazyStaggeredGridMeasureContext.findNextItemIndex(item: Int, lane: Int): Int =
+ spans.findNextItemIndex(item, lane)
+
@OptIn(ExperimentalFoundationApi::class)
private class LazyStaggeredGridMeasureProvider(
private val isVertical: Boolean,
@@ -479,7 +573,7 @@
private val resolvedSlotSums: IntArray,
private val measuredItemFactory: MeasuredItemFactory
) {
- fun childConstraints(slot: Int): Constraints {
+ private fun childConstraints(slot: Int): Constraints {
val previousSum = if (slot == 0) 0 else resolvedSlotSums[slot - 1]
val crossAxisSize = resolvedSlotSums[slot] - previousSum
return if (isVertical) {
@@ -496,6 +590,15 @@
}
}
+// This interface allows to avoid autoboxing on index param
+private fun interface MeasuredItemFactory {
+ fun createItem(
+ index: Int,
+ key: Any,
+ placeables: List<Placeable>
+ ): LazyStaggeredGridMeasuredItem
+}
+
private class LazyStaggeredGridMeasuredItem(
val index: Int,
val key: Any,
@@ -511,6 +614,7 @@
}!!
fun position(
+ lane: Int,
mainAxis: Int,
crossAxis: Int,
): LazyStaggeredGridPositionedItem =
@@ -520,6 +624,7 @@
} else {
IntOffset(mainAxis, crossAxis)
},
+ lane = lane,
index = index,
key = key,
size = IntSize(sizeWithSpacings, crossAxisSize),
@@ -530,6 +635,7 @@
private class LazyStaggeredGridPositionedItem(
override val offset: IntOffset,
override val index: Int,
+ override val lane: Int,
override val key: Any,
override val size: IntSize,
private val placeables: List<Placeable>
@@ -540,12 +646,3 @@
}
}
}
-
-// This interface allows to avoid autoboxing on index param
-private fun interface MeasuredItemFactory {
- fun createItem(
- index: Int,
- key: Any,
- placeables: List<Placeable>
- ): LazyStaggeredGridMeasuredItem
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
new file mode 100644
index 0000000..43b03ef
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.checkScrollableContainerConstraints
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+
+@Composable
+@ExperimentalFoundationApi
+internal fun rememberStaggeredGridMeasurePolicy(
+ state: LazyStaggeredGridState,
+ itemProvider: LazyLayoutItemProvider,
+ contentPadding: PaddingValues,
+ reverseLayout: Boolean,
+ orientation: Orientation,
+ verticalArrangement: Arrangement.Vertical,
+ horizontalArrangement: Arrangement.Horizontal,
+ slotSizesSums: Density.(Constraints) -> IntArray,
+ overscrollEffect: OverscrollEffect
+): LazyLayoutMeasureScope.(Constraints) -> LazyStaggeredGridMeasureResult = remember(
+ state,
+ itemProvider,
+ contentPadding,
+ reverseLayout,
+ orientation,
+ verticalArrangement,
+ horizontalArrangement,
+ slotSizesSums,
+ overscrollEffect,
+) {
+ { constraints ->
+ checkScrollableContainerConstraints(
+ constraints,
+ orientation
+ )
+ val resolvedSlotSums = slotSizesSums(this, constraints)
+
+ // setup information for prefetch
+ state.prefetchLaneWidths = resolvedSlotSums
+ state.isVertical = orientation == Orientation.Vertical
+
+ measure(
+ state,
+ itemProvider,
+ resolvedSlotSums,
+ constraints,
+ isVertical = orientation == Orientation.Vertical,
+ beforeContentPadding = 0,
+ afterContentPadding = 0,
+ ).also {
+ state.applyMeasureResult(it)
+ overscrollEffect.isEnabled = it.canScrollForward || it.canScrollBackward
+ }
+ }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
index 4abccf9..a01732e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
@@ -27,12 +27,24 @@
val measureResult: MeasureResult,
val canScrollForward: Boolean,
val canScrollBackward: Boolean,
- val visibleItemsInfo: Array<List<LazyStaggeredGridItemInfo>>
-) : MeasureResult by measureResult
+ override val totalItemsCount: Int,
+ override val visibleItemsInfo: List<LazyStaggeredGridItemInfo>
+) : LazyStaggeredGridLayoutInfo, MeasureResult by measureResult
internal interface LazyStaggeredGridItemInfo {
val offset: IntOffset
val index: Int
+ val lane: Int
val key: Any
val size: IntSize
+}
+
+internal interface LazyStaggeredGridLayoutInfo {
+ val visibleItemsInfo: List<LazyStaggeredGridItemInfo>
+ val totalItemsCount: Int
+
+ companion object Empty : LazyStaggeredGridLayoutInfo {
+ override val visibleItemsInfo: List<LazyStaggeredGridItemInfo> = emptyList()
+ override val totalItemsCount: Int = 0
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt
new file mode 100644
index 0000000..acb28d0
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+/**
+ * Utility class to remember grid lane assignments (spans) in a sliding window relative to requested
+ * item position (usually reflected by scroll position).
+ * Remembers the maximum range of remembered items is reflected by [MaxCapacity], if index is beyond
+ * the bounds, [anchor] moves to reflect new position.
+ */
+internal class LazyStaggeredGridSpans {
+ private var anchor = 0
+ private var spans = IntArray(16)
+
+ /**
+ * Sets given span for given item index.
+ */
+ fun setSpan(item: Int, span: Int) {
+ require(item >= 0) { "Negative spans are not supported" }
+ ensureValidIndex(item)
+ spans[item - anchor] = span + 1
+ }
+
+ /**
+ * Get span for given item index.
+ * @return span previously recorded for given item or [Unset] if it doesn't exist.
+ */
+ fun getSpan(item: Int): Int {
+ if (item < lowerBound() || item >= upperBound()) {
+ return Unset
+ }
+ return spans[item - anchor] - 1
+ }
+
+ /**
+ * @return upper bound of currently valid span range
+ */
+ /* @VisibleForTests */
+ fun upperBound(): Int = anchor + spans.size
+
+ /**
+ * @return lower bound of currently valid span range
+ */
+ /* @VisibleForTests */
+ fun lowerBound(): Int = anchor
+
+ /**
+ * Delete remembered span assignments.
+ */
+ fun reset() {
+ spans.fill(0)
+ }
+
+ /**
+ * Find the previous item relative to [item] set to target span
+ * @return found item index or [Unset] if it doesn't exist.
+ */
+ fun findPreviousItemIndex(item: Int, target: Int): Int {
+ for (i in (item - 1) downTo 0) {
+ val span = getSpan(i)
+ if (span == target || span == Unset) {
+ return i
+ }
+ }
+ return Unset
+ }
+
+ /**
+ * Find the next item relative to [item] set to target span
+ * @return found item index or [upperBound] if it doesn't exist.
+ */
+ fun findNextItemIndex(item: Int, target: Int): Int {
+ for (i in item + 1 until upperBound()) {
+ val span = getSpan(i)
+ if (span == target || span == Unset) {
+ return i
+ }
+ }
+ return upperBound()
+ }
+
+ private fun ensureValidIndex(requestedIndex: Int) {
+ val requestedCapacity = requestedIndex - anchor
+
+ if (requestedCapacity in 0 until MaxCapacity) {
+ // simplest path - just grow array to given capacity
+ ensureCapacity(requestedCapacity + 1)
+ } else {
+ // requested index is beyond current span bounds
+ // rebase anchor so that requested index is in the middle of span array
+ val oldAnchor = anchor
+ anchor = maxOf(requestedIndex - (spans.size / 2), 0)
+ var delta = anchor - oldAnchor
+
+ if (delta >= 0) {
+ // copy previous span data if delta is smaller than span size
+ if (delta < spans.size) {
+ spans.copyInto(
+ spans,
+ destinationOffset = 0,
+ startIndex = delta,
+ endIndex = spans.size
+ )
+ }
+ // fill the rest of the spans with default values
+ spans.fill(0, maxOf(0, spans.size - delta), spans.size)
+ } else {
+ delta = -delta
+ // check if we can grow spans to match delta
+ if (spans.size + delta < MaxCapacity) {
+ // grow spans and leave space in the start
+ ensureCapacity(spans.size + delta + 1, delta)
+ } else {
+ // otherwise, just move data that fits
+ if (delta < spans.size) {
+ spans.copyInto(
+ spans,
+ destinationOffset = delta,
+ startIndex = 0,
+ endIndex = spans.size - delta
+ )
+ }
+ // fill the rest of the spans with default values
+ spans.fill(0, 0, minOf(spans.size, delta))
+ }
+ }
+ }
+ }
+
+ private fun ensureCapacity(capacity: Int, newOffset: Int = 0) {
+ require(capacity <= MaxCapacity) {
+ "Requested span capacity $capacity is larger than max supported: $MaxCapacity!"
+ }
+ if (spans.size < capacity) {
+ var newSize = spans.size
+ while (newSize < capacity) newSize *= 2
+ spans = spans.copyInto(IntArray(newSize), destinationOffset = newOffset)
+ }
+ }
+
+ companion object {
+ private const val MaxCapacity = 131_072 // Closest to 100_000, 2 ^ 17
+ internal const val Unset = -1
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index cef7880..e413576 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -21,22 +21,33 @@
import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
+import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle
+import androidx.compose.runtime.collection.mutableVectorOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.layout.Remeasurement
import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.unit.Constraints
import kotlin.math.abs
@ExperimentalFoundationApi
-internal class LazyStaggeredGridState : ScrollableState {
- var firstVisibleItems: IntArray by mutableStateOf(IntArray(0))
+internal class LazyStaggeredGridState(
+ initialFirstVisibleItems: IntArray = IntArray(0),
+ initialFirstVisibleOffsets: IntArray = IntArray(0)
+) : ScrollableState {
+ var firstVisibleItems: IntArray by mutableStateOf(initialFirstVisibleItems)
private set
- var firstVisibleItemScrollOffsets: IntArray by mutableStateOf(IntArray(0))
+ var firstVisibleItemScrollOffsets: IntArray by mutableStateOf(initialFirstVisibleOffsets)
private set
- internal val spans: SpanLookup = SpanLookup()
+ internal var layoutInfo: LazyStaggeredGridLayoutInfo by mutableStateOf(
+ LazyStaggeredGridLayoutInfo.Empty
+ )
+ private set
+
+ internal val spans = LazyStaggeredGridSpans()
private var canScrollForward = true
private var canScrollBackward = true
@@ -61,6 +72,12 @@
/* @VisibleForTesting */
internal var measurePassCount = 0
+ private var wasScrollingForward = true
+ internal var isVertical = false
+ internal var prefetchLaneWidths: IntArray = IntArray(0)
+ private var prefetchBaseIndex: Int = -1
+ private val currentItemPrefetchHandles = mutableVectorOf<PrefetchHandle>()
+
override suspend fun scroll(
scrollPriority: MutatePriority,
block: suspend ScrollScope.() -> Unit
@@ -81,12 +98,11 @@
// inside measuring we do scrollToBeConsumed.roundToInt() so there will be no scroll if
// we have less than 0.5 pixels
if (abs(scrollToBeConsumed) > 0.5f) {
+ val preScrollToBeConsumed = scrollToBeConsumed
remeasurement?.forceRemeasure()
- // todo(b/182882362): notify prefetch
-// if (prefetchingEnabled) {
-// val leftoverScroll = preScrollToBeConsumed - scrollToBeConsumed
-// notifyPrefetch(preScrollToBeConsumed - scrollToBeConsumed)
-// }
+ if (prefetchingEnabled) {
+ notifyPrefetch(preScrollToBeConsumed - scrollToBeConsumed)
+ }
}
// here scrollToBeConsumed is already consumed during the forceRemeasure invocation
@@ -106,45 +122,95 @@
override fun dispatchRawDelta(delta: Float): Float =
scrollableState.dispatchRawDelta(delta)
+ private fun notifyPrefetch(delta: Float) {
+ if (!prefetchingEnabled) {
+ return
+ }
+ val info = layoutInfo
+ if (info.visibleItemsInfo.isNotEmpty()) {
+ val scrollingForward = delta < 0
+
+ if (wasScrollingForward != scrollingForward) {
+ // the scrolling direction has been changed which means the last prefetched item
+ // is not going to be reached anytime soon so it is safer to dispose it.
+ // if this item is already visible it is safe to call the method anyway
+ // as it will be a no-op
+ currentItemPrefetchHandles.forEach { it.cancel() }
+ }
+
+ wasScrollingForward = scrollingForward
+ val prefetchIndex = if (scrollingForward) {
+ info.visibleItemsInfo.last().index
+ } else {
+ info.visibleItemsInfo.first().index
+ }
+
+ if (prefetchIndex == prefetchBaseIndex) {
+ // Already prefetched based on this index
+ return
+ }
+ prefetchBaseIndex = prefetchIndex
+ currentItemPrefetchHandles.clear()
+
+ var targetIndex = prefetchIndex
+ for (lane in prefetchLaneWidths.indices) {
+ val previousIndex = targetIndex
+
+ // find the next item for each line and prefetch if it is valid
+ targetIndex = if (scrollingForward) {
+ spans.findNextItemIndex(previousIndex, lane)
+ } else {
+ spans.findPreviousItemIndex(previousIndex, lane)
+ }
+ if (
+ targetIndex !in (0 until info.totalItemsCount) ||
+ previousIndex == targetIndex
+ ) {
+ return
+ }
+
+ val crossAxisSize = prefetchLaneWidths[lane] -
+ if (lane == 0) 0 else prefetchLaneWidths[lane - 1]
+
+ val constraints = if (isVertical) {
+ Constraints.fixedWidth(crossAxisSize)
+ } else {
+ Constraints.fixedHeight(crossAxisSize)
+ }
+
+ currentItemPrefetchHandles.add(
+ prefetchState.schedulePrefetch(
+ index = targetIndex,
+ constraints = constraints
+ )
+ )
+ }
+ }
+ }
+
+ private fun cancelPrefetchIfVisibleItemsChanged(info: LazyStaggeredGridLayoutInfo) {
+ val items = info.visibleItemsInfo
+ if (prefetchBaseIndex != -1 && items.isNotEmpty()) {
+ if (prefetchBaseIndex !in items.first().index..items.last().index) {
+ prefetchBaseIndex = -1
+ currentItemPrefetchHandles.forEach { it.cancel() }
+ currentItemPrefetchHandles.clear()
+ }
+ }
+ }
+
internal fun applyMeasureResult(result: LazyStaggeredGridMeasureResult) {
scrollToBeConsumed -= result.consumedScroll
firstVisibleItems = result.firstVisibleItemIndices
firstVisibleItemScrollOffsets = result.firstVisibleItemScrollOffsets
canScrollBackward = result.canScrollBackward
canScrollForward = result.canScrollForward
+ layoutInfo = result
+ cancelPrefetchIfVisibleItemsChanged(result)
measurePassCount++
}
override val isScrollInProgress: Boolean
get() = scrollableState.isScrollInProgress
-}
-
-internal class SpanLookup {
- private var spans = IntArray(16)
-
- fun setSpan(item: Int, span: Int) {
- ensureCapacity(item + 1)
- spans[item] = span + 1
- }
-
- fun getSpan(item: Int): Int =
- spans[item] - 1
-
- fun capacity(): Int =
- spans.size
-
- private fun ensureCapacity(capacity: Int) {
- if (spans.size < capacity) {
- spans = spans.copyInto(IntArray(spans.size * 2))
- }
- }
-
- fun reset() {
- spans.fill(0)
- }
-
- companion object {
- internal const val SpanUnset = -1
- }
}
\ No newline at end of file
diff --git a/docs/OWNERS b/docs/OWNERS
new file mode 100644
index 0000000..636ac16
--- /dev/null
+++ b/docs/OWNERS
@@ -0,0 +1,2 @@
[email protected]
[email protected]
diff --git a/docs/api_guidelines/deprecation.md b/docs/api_guidelines/deprecation.md
index ae4246a..0ca80db 100644
--- a/docs/api_guidelines/deprecation.md
+++ b/docs/api_guidelines/deprecation.md
@@ -5,12 +5,22 @@
API, there are many other ways to influence how developers interact with your
library.
-### Deprecation (`@deprecated`)
+### Deprecation (`@Deprecated`)
Deprecation lets a developer know that they should stop using an API or class.
-All deprecations must be marked with a `@Deprecated` Java annotation as well as
-a `@deprecated <migration-docs>` docs annotation explaining how the developer
-should migrate away from the API.
+All deprecations must be marked with a `@Deprecated` code annotation as well as
+a `@deprecated <explanation>` docs annotation (for Java) or
+[`@Deprecated(message = <explanation>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/)
+(for Kotlin) explaining the rationale and how the developer should migrate away
+from the API.
+
+Deprecations in Kotlin are encouraged to provide an automatic migration by
+specifying the
+[`replaceWith = ReplaceWith(<replacement>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-replace-with/)
+parameter to `@Deprecated` in cases where the migration is a straightforward
+replacement and *may* specify
+[`level = DeprecationLevel.ERROR`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecation-level/)
+(source-breaking) in cases where the API on track to be fully removed.
Deprecation is an non-breaking API change that must occur in a **major** or
**minor** release.
@@ -19,7 +29,7 @@
within the same cycle, e.g. added in `alpha01` and deprecated in `alpha06`,
[must be removed](versioning.md#beta-checklist) before moving to `beta01`.
-### Soft removal (@removed)
+### Soft removal (`@removed` or `DeprecationLevel.HIDDEN`)
Soft removal preserves binary compatibility while preventing source code from
compiling against an API. It is a *source-breaking change* and not recommended.
@@ -27,8 +37,11 @@
Soft removals **must** do the following:
* Mark the API as deprecated for at least one stable release prior to removal.
-* Mark the API with a `@RestrictTo(LIBRARY)` Java annotation as well as a
- `@removed <reason>` docs annotation explaining why the API was removed.
+* In Java sources, mark the API with a `@RestrictTo(LIBRARY)` Java annotation
+ as well as a `@removed <reason>` docs annotation explaining why the API was
+ removed.
+* In Kotlin sources, mark the API with `@Deprecated(message = <reason>,
+ level = DeprecationLevel.HIDDEN)` explaining why the API was removed.
* Maintain binary compatibility, as the API may still be called by existing
dependent libraries.
* Maintain behavioral compatibility and existing tests.
diff --git a/docs/api_guidelines/platform_compat.md b/docs/api_guidelines/platform_compat.md
index 6de372e..0148e18 100644
--- a/docs/api_guidelines/platform_compat.md
+++ b/docs/api_guidelines/platform_compat.md
@@ -1,24 +1,34 @@
## Platform compatibility API patterns {#platform-compatibility-apis}
-NOTE For all library APIs that wrap or provide parity with platform APIs,
-*parity with the platform APIs overrides API guidelines*. For example, if the
-platform API being wrapped has incorrect `Executor` and `Callback` ordering
-according to the API Guidelines, the corresponding library API should have the
-exact same (incorrect) ordering.
+NOTE For all library APIs that wrap or provide parity with platform APIs, we
+prefer to follow modern API guidelines; however, developers *may* choose to
+prioritize parity with the platform APIs over adherence to modern guidelines.
+For example, if the platform API being wrapped has incorrect `Executor` and
+`Callback` ordering according to the API Guidelines, the corresponding library
+API *should* re-order the arguments.
-### Static shims (ex. [ViewCompat](https://developer.android.com/reference/android/support/v4/view/ViewCompat.html)) {#static-shim}
+### Static shims (ex. [ViewCompat](https://developer.android.com/reference/androidx/core/view/ViewCompat)) {#static-shim}
When to use?
* Platform class exists at module's `minSdkVersion`
* Compatibility implementation does not need to store additional metadata
-Implementation requirements
+#### API guidelines {#static-shim-api-guidelines}
+##### Naming {#static-shim-naming}
+
+* Class *should* be added to the `androidx.core:core` library
* Class name **must** be `<PlatformClass>Compat`
* Package name **must** be `androidx.<feature>.<platform.package>`
* Superclass **must** be `Object`
+
+##### Construction {#static-shim-construction}
+
* Class **must** be non-instantiable, i.e. constructor is private no-op
+
+#### Implementation {#static-shim-implementation}
+
* Static fields and static methods **must** match match signatures with
`<PlatformClass>`
* Static fields that can be inlined, ex. integer constants, **must not**
@@ -34,7 +44,9 @@
The following sample provides static helper methods for the platform class
`android.os.Process`.
-```java
+~~~java
+package androidx.core.os;
+
/**
* Helper for accessing features in {@link Process}.
*/
@@ -61,51 +73,16 @@
public static boolean isApplicationUid(int uid) {
if (Build.VERSION.SDK_INT >= 24) {
return Api24Impl.isApplicationUid(uid);
- } else if (Build.VERSION.SDK_INT >= 17) {
- return Api17Impl.isApplicationUid(uid);
- } else if (Build.VERSION.SDK_INT == 16) {
- return Api16Impl.isApplicationUid(uid);
+ } else if (Build.VERSION.SDK_INT >= 16) {
+ // Fall back to using reflection on private APIs.
+ // ...
} else {
return true;
}
}
-
- @RequiresApi(24)
- static class Api24Impl {
- static boolean isApplicationUid(int uid) {
- // In N, the method was made public on android.os.Process.
- return Process.isApplicationUid(uid);
- }
- }
-
- @RequiresApi(17)
- static class Api17Impl {
- private static Method sMethod_isAppMethod;
- private static boolean sResolved;
-
- static boolean isApplicationUid(int uid) {
- // In JELLY_BEAN_MR2, the equivalent isApp(int) hidden method moved to public class
- // android.os.UserHandle.
- try {
- if (!sResolved) {
- sResolved = true;
- sMethod_isAppMethod = UserHandle.class.getDeclaredMethod("isApp",int.class);
- }
- if (sMethod_isAppMethod != null) {
- return (Boolean) sMethod_isAppMethod.invoke(null, uid);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return true;
- }
- }
-
- ...
}
-```
-### Wrapper (ex. [AccessibilityNodeInfoCompat](https://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.html)) {#wrapper}
+### Wrapper (ex. [AccessibilityNodeInfoCompat](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo)) {#wrapper}
When to use?
@@ -116,6 +93,42 @@
very difficult to deprecate classes and migrate source code when the
`minSdkVersion` is raised
+#### API guidelines {#wrapper-api-guidelines}
+
+##### Naming {#wrapper-naming}
+
+* Class name **must** be `<PlatformClass>Compat`
+* Package name **must** be `androidx.core.<platform.package>`
+* Superclass **must not** be `<PlatformClass>`
+
+##### Construction {#wrapper-construction}
+
+* Class *may* have public constructor(s) to provide parity with public
+ `PlatformClass` constructors
+ * Constructor used to wrap `PlatformClass` **must not** be public
+* Class **must** implement a static `PlatformClassCompat
+ toPlatformClassCompat(PlatformClass)` method to wrap `PlatformClass` on
+ supported SDK levels
+ * If class does not exist at module's `minSdkVersion`, method must be
+ annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+ introduced
+
+#### Implementation {#wrapper-implementation}
+
+* Class *should* be added to the `androidx.core:core` library
+* Class **must** implement a `PlatformClass toPlatformClass()` method to
+ unwrap `PlatformClass` on supported SDK levels
+ * If class does not exist at module's `minSdkVersion`, method must be
+ annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+ introduced
+* Implementation *may* delegate to `PlatformClass` methods when available (see
+ below note for caveats)
+* To avoid runtime class verification issues, all operations that interact
+ with the internal structure of `PlatformClass` must be implemented in inner
+ classes targeted to the SDK level at which the operation was added.
+ * See the [sample](#wrapper-sample) for an example of interacting with a
+ method that was added in SDK level 23.
+
#### Sample {#wrapper-sample}
The following sample wraps a hypothetical platform class `ModemInfo` that was
@@ -126,7 +139,7 @@
// Only guaranteed to be non-null on SDK_INT >= 23. Note that referencing the
// class itself directly is fine -- only references to class members need to
// be pushed into static inner classes.
- private final ModemInfo wrappedObj;
+ private final Object wrappedObj;
/**
* [Copy platform docs for matching constructor.]
@@ -137,7 +150,6 @@
} else {
wrappedObj = null;
}
- ...
}
@RequiresApi(23)
@@ -192,7 +204,7 @@
return Api23Impl.isLteSupported(mWrapped);
} else if (SDK_INT >= 18) {
// Smart fallback behavior based on earlier APIs.
- ...
+ // ...
}
// Default behavior.
return false;
@@ -210,12 +222,12 @@
}
@DoNotInline
- static boolean isLteSupported(ModemInfo obj) {
- return obj.isLteSupported();
+ static boolean isLteSupported(Object obj) {
+ return ((ModemInfo) obj).isLteSupported();
}
}
}
-```
+~~~
Note that libraries written in Java should express conversion to and from the
platform class differently than Kotlin classes. For Java classes, conversion
@@ -254,91 +266,7 @@
}
```
-#### API guidelines {#wrapper-api-guidelines}
-
-##### Naming {#wrapper-naming}
-
-* Class name **must** be `<PlatformClass>Compat`
-* Package name **must** be `androidx.core.<platform.package>`
-* Superclass **must not** be `<PlatformClass>`
-
-##### Construction {#wrapper-construction}
-
-* Class *may* have public constructor(s) to provide parity with public
- `PlatformClass` constructors
- * Constructor used to wrap `PlatformClass` **must not** be public
-* Class **must** implement a static `PlatformClassCompat
- toPlatformClassCompat(PlatformClass)` method to wrap `PlatformClass` on
- supported SDK levels
- * If class does not exist at module's `minSdkVersion`, method must be
- annotated with `@RequiresApi(<sdk>)` for SDK version where class was
- introduced
-
-#### Implementation {#wrapper-implementation}
-
-* Class **must** implement a `PlatformClass toPlatformClass()` method to
- unwrap `PlatformClass` on supported SDK levels
- * If class does not exist at module's `minSdkVersion`, method must be
- annotated with `@RequiresApi(<sdk>)` for SDK version where class was
- introduced
-* Implementation *may* delegate to `PlatformClass` methods when available (see
- below note for caveats)
-* To avoid runtime class verification issues, all operations that interact
- with the internal structure of `PlatformClass` must be implemented in inner
- classes targeted to the SDK level at which the operation was added.
- * See the [sample](#wrapper-sample) for an example of interacting with a
- method that was added in SDK level 23.
-
-### Safe super. invocation {#safe-super-calls}
-
-When to use?
-
-* When invoking `method.superMethodIntroducedSinceMinSdk()`
-
-Implementation requirements
-
-* Class must be a *non-static* **inner class** (captures `this` pointer)
-* Class may not be exposed in public API
-
-This should only be used when calling `super` methods that will not verify (such
-as when overriding a new method to provide back compat).
-
-Super calls is not available in a `static` context in Java. It can however be
-called from an inner class.
-
-#### Sample {#safe-super-calls-sample}
-
-```java
-class AppCompatTextView : TextView {
-
- @Nullable
- SuperCaller mSuperCaller = null;
-
- @Override
- int getPropertyFromApi99() {
- if (Build.VERSION.SDK_INT > 99) {
- getSuperCaller().getPropertyFromApi99)();
- }
-
- @NonNull
- @RequiresApi(99)
- SuperCaller getSuperCaller() {
- if (mSuperCaller == null) {
- mSuperCaller = new SuperCaller();
- }
- return mSuperCaller;
- }
-
- @RequiresApi(99)
- class SuperCaller {
- int getPropertyFromApi99() {
- return AppCompatTextView.super.getPropertyFromApi99();
- }
- }
-}
-```
-
-### Standalone (ex. [ArraySet](https://developer.android.com/reference/android/support/v4/util/ArraySet.html), [Fragment](https://developer.android.com/reference/android/support/v4/app/Fragment.html)) {#standalone}
+### Standalone (ex. [ArraySet](https://developer.android.com/reference/androidx/collection/ArraySet), [Fragment](https://developer.android.com/jetpack/androidx/releases/fragment)) {#standalone}
When to use?
@@ -361,7 +289,7 @@
`static toCompat<PlatformClass>` method naming convention.
* Implementation *may* delegate to `PlatformClass` methods when available
-### Standalone JAR library (no Android dependencies) {#standalone-jar-library-no-android-dependencies}
+### Standalone JAR library (no Android dependencies) {#standalone-jvm}
When to use
@@ -392,3 +320,52 @@
the fact that Room generates code dynamically, means that Room interfaces can be
used in host-side tests (though actual DB code should be tested on device, since
DB impls may be significantly different on host).
+
+### Addressing class verification failures on `super.` invocation {#compat-super}
+
+Invoking a `super` call on a method introduced in an API level higher than a
+class's minimum SDK level will raise a run-time class verification failure, and
+will be detected by the `ClassVerificationFailure` lint check.
+
+```java {.bad}
+public void performAction() {
+ if (SDK_INT >= 31) {
+ super.performAction(); // This will cause a verification failure.
+ }
+}
+```
+
+These failures can be addressed by out-of-lining the `super` call to a
+non-static inner class.
+
+#### Sample {#compat-super-sample}
+
+```java
+class AppCompatTextView : TextView {
+
+ @Nullable
+ SuperCaller mSuperCaller = null;
+
+ @Override
+ int getPropertyFromApi99() {
+ if (Build.VERSION.SDK_INT > 99) {
+ getSuperCaller().getPropertyFromApi99)();
+ }
+
+ @NonNull
+ @RequiresApi(99)
+ private SuperCaller getSuperCaller() {
+ if (mSuperCaller == null) {
+ mSuperCaller = new Api99SuperCaller();
+ }
+ return mSuperCaller;
+ }
+
+ @RequiresApi(99)
+ private class Api99SuperCaller {
+ int getPropertyFromApi99() {
+ return AppCompatTextView.super.getPropertyFromApi99();
+ }
+ }
+}
+```
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7d66694..bbab205 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -23,6 +23,7 @@
atomicFu = "0.17.0"
autoService = "1.0-rc6"
autoValue = "1.6.3"
+asm = "9.3"
cmake = "3.22.1"
dagger = "2.42"
dexmaker = "2.28.3"
@@ -74,6 +75,8 @@
apacheCommonsCodec = { module = "commons-codec:commons-codec", version = "1.15" }
apacheCommonIo = { module = "commons-io:commons-io", version = "2.4" }
assertj = { module = "org.assertj:assertj-core", version = "3.11.1" }
+asm = { module = "org.ow2.asm:asm", version = "asm"}
+asmCommons = { module = "org.ow2.asm:asm-commons", version = "asm"}
checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
diff --git a/lifecycle/lifecycle-common/build.gradle b/lifecycle/lifecycle-common/build.gradle
index c62e819..2e59a5d 100644
--- a/lifecycle/lifecycle-common/build.gradle
+++ b/lifecycle/lifecycle-common/build.gradle
@@ -29,7 +29,7 @@
constraints {
implementation(project(":lifecycle:lifecycle-common-java8"))
- implementation(project(":lifecycle:lifecycle-runtime"))
+ implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
}
}
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index 85c3906..a46628b 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -44,6 +44,7 @@
androidTestImplementation(libs.truth)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRules)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.kotlinCoroutinesTest)
androidTestImplementation(libs.kotlinCoroutinesAndroid)
diff --git a/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt b/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
index 66dd70c..315032a 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
@@ -17,6 +17,7 @@
package androidx.lifecycle
import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
@@ -24,6 +25,7 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
@@ -67,4 +69,16 @@
stopChannelFlow.complete(Unit)
}
}
+
+ @UiThreadTest
+ @Test
+ @MediumTest
+ fun asLiveData_preserveStateFlowInitialValue() {
+ val value = "init"
+ val result = MutableStateFlow(value)
+ .asLiveData()
+ .value
+ assertThat(result).isNotNull()
+ assertThat(result).isEqualTo(value)
+ }
}
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
index 9c907b9..7c2d8ed 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
@@ -20,6 +20,7 @@
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.arch.core.executor.ArchTaskExecutor
import java.time.Duration
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -28,7 +29,7 @@
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -36,6 +37,9 @@
/**
* Creates a LiveData that has values collected from the origin [Flow].
*
+ * If the origin [Flow] is a [StateFlow], then the initial value will be populated
+ * to the [LiveData]'s value field on the main thread.
+ *
* The upstream flow collection starts when the returned [LiveData] becomes active
* ([LiveData.onActive]).
* If the [LiveData] becomes inactive ([LiveData.onInactive]) while the flow has not completed,
@@ -75,6 +79,15 @@
collect {
emit(it)
}
+}.also { liveData ->
+ val flow = this
+ if (flow is StateFlow<T>) {
+ if (ArchTaskExecutor.getInstance().isMainThread) {
+ liveData.value = flow.value
+ } else {
+ liveData.postValue(flow.value)
+ }
+ }
}
/**
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 22916dc..a88e0f2 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -112,7 +112,7 @@
androidTestImplementation(project(":internal-testutils-common"))
androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
androidTestImplementation("androidx.paging:paging-runtime:3.1.1")
- androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
+ androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
androidTestImplementation(libs.rxjava2)
testImplementation(libs.mockitoCore)
}
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
index 7d76076..8907b5e 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.graphics.Point;
import android.graphics.Rect;
import androidx.test.filters.FlakyTest;
@@ -678,10 +679,24 @@
assertThrows(IllegalStateException.class, () -> smallArea.pinchIn(100, 10));
}
- /* TODO(b/241158642): Implement these tests, and the tests for exceptions of each tested method.
+ @Test
+ public void testPerformTwoPointerGesture_withZeroSteps() throws Exception {
+ // Note that most part of `performTwoPointerGesture` (and `performMultiPointerGesture`)
+ // has already been indirectly tested in other tests. This test only test the case when
+ // the `step` parameter is set to zero.
+ launchTestActivity(PointerGestureTestActivity.class);
- public void testPerformTwoPointerGesture() {}
+ UiObject touchRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/touch_region"));
- public void testPerformMultiPointerGesture() {}
- */
+ Rect visibleBounds = touchRegion.getVisibleBounds();
+ Point startPoint1 = new Point(visibleBounds.left + 50, visibleBounds.top + 50);
+ Point startPoint2 = new Point(visibleBounds.right - 50, visibleBounds.top + 50);
+ Point endPoint1 = new Point(visibleBounds.left + 50, visibleBounds.bottom - 50);
+ Point endPoint2 = new Point(visibleBounds.right - 50, visibleBounds.bottom - 50);
+
+ assertTrue(touchRegion.performTwoPointerGesture(startPoint1, startPoint2, endPoint1,
+ endPoint2, 0));
+ assertEquals("2 touch(es) received", touchRegion.getText());
+ }
}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
index 2e3d6d6..07b71cf 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -134,6 +134,13 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
+ <activity android:name=".PointerGestureTestActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.Holo.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
<activity android:name=".SplitScreenTestActivity"
android:exported="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java
new file mode 100644
index 0000000..249f805
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class PointerGestureTestActivity extends Activity {
+ private int mPointerGestureCount = 0;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.pointer_gesture_test_activity);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ int maskedAction = e.getActionMasked();
+
+ if (maskedAction == MotionEvent.ACTION_DOWN
+ || maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
+ mPointerGestureCount++;
+ }
+
+ if (maskedAction == MotionEvent.ACTION_UP) {
+ TextView touchRegion = findViewById(R.id.touch_region);
+ touchRegion.setText(String.format("%d touch(es) received", mPointerGestureCount));
+ }
+
+ return true;
+ }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml
new file mode 100644
index 0000000..5c6125c
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".PointerGestureTestActivity">
+
+ <TextView
+ android:id="@+id/touch_region"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center" />
+
+</LinearLayout>
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -143,32 +143,32 @@
method public void clearLastTraversedText();
method public boolean click(int, int);
method public boolean drag(int, int, int, int, int);
- method @Deprecated public void dumpWindowHierarchy(String!);
- method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
- method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
- method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+ method @Deprecated public void dumpWindowHierarchy(String);
+ method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+ method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+ method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
method public void freezeRotation() throws android.os.RemoteException;
method @Deprecated public String! getCurrentActivityName();
method public String! getCurrentPackageName();
method public int getDisplayHeight();
method public int getDisplayRotation();
- method public android.graphics.Point! getDisplaySizeDp();
+ method public android.graphics.Point getDisplaySizeDp();
method public int getDisplayWidth();
- method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
- method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+ method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+ method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
method public String! getLastTraversedText();
method public String! getLauncherPackageName();
- method public String! getProductName();
+ method public String getProductName();
method public boolean hasAnyWatcherTriggered();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
- method public boolean hasWatcherTriggered(String!);
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
+ method public boolean hasWatcherTriggered(String?);
method public boolean isNaturalOrientation();
method public boolean isScreenOn() throws android.os.RemoteException;
method public boolean openNotification();
method public boolean openQuickSettings();
- method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+ method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
method public boolean pressBack();
method public boolean pressDPadCenter();
method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
method public boolean pressMenu();
method public boolean pressRecentApps() throws android.os.RemoteException;
method public boolean pressSearch();
- method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
- method public void removeWatcher(String!);
+ method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+ method public void removeWatcher(String?);
method public void resetWatcherTriggers();
method public void runWatchers();
method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
method public void setOrientationRight() throws android.os.RemoteException;
method public void sleep() throws android.os.RemoteException;
method public boolean swipe(int, int, int, int, int);
- method public boolean swipe(android.graphics.Point![]!, int);
- method public boolean takeScreenshot(java.io.File!);
- method public boolean takeScreenshot(java.io.File!, float, int);
+ method public boolean swipe(android.graphics.Point![], int);
+ method public boolean takeScreenshot(java.io.File);
+ method public boolean takeScreenshot(java.io.File, float, int);
method public void unfreezeRotation() throws android.os.RemoteException;
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
method public void waitForIdle();
method public void waitForIdle(long);
- method public boolean waitForWindowUpdate(String!, long);
+ method public boolean waitForWindowUpdate(String?, long);
method public void wakeUp() throws android.os.RemoteException;
}
@@ -212,20 +212,20 @@
method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean exists();
- method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
- method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+ method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public final androidx.test.uiautomator.UiSelector! getSelector();
- method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public final androidx.test.uiautomator.UiSelector getSelector();
+ method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
- method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+ method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
public class UiObject2 {
method public void clear();
method public void click();
- method public void click(android.graphics.Point!);
+ method public void click(android.graphics.Point);
method public void click(long);
- method public void click(android.graphics.Point!, long);
- method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
- method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
- method public void drag(android.graphics.Point!);
- method public void drag(android.graphics.Point!, int);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public boolean fling(androidx.test.uiautomator.Direction!);
- method public boolean fling(androidx.test.uiautomator.Direction!, int);
+ method public void click(android.graphics.Point, long);
+ method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+ method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+ method public void drag(android.graphics.Point);
+ method public void drag(android.graphics.Point, int);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+ method public boolean fling(androidx.test.uiautomator.Direction);
+ method public boolean fling(androidx.test.uiautomator.Direction, int);
method public String! getApplicationPackage();
method public int getChildCount();
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
method public String! getClassName();
method public String! getContentDescription();
method public int getDisplayId();
method public androidx.test.uiautomator.UiObject2! getParent();
method public String! getResourceName();
method public String! getText();
- method public android.graphics.Rect! getVisibleBounds();
- method public android.graphics.Point! getVisibleCenter();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+ method public android.graphics.Rect getVisibleBounds();
+ method public android.graphics.Point getVisibleCenter();
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -298,15 +298,15 @@
method public void pinchOpen(float);
method public void pinchOpen(float, int);
method public void recycle();
- method public boolean scroll(androidx.test.uiautomator.Direction!, float);
- method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
method public void setGestureMargin(int);
method public void setGestureMargins(int, int, int, int);
- method public void setText(String!);
- method public void swipe(androidx.test.uiautomator.Direction!, float);
- method public void swipe(androidx.test.uiautomator.Direction!, float, int);
- method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public void setText(String?);
+ method public void swipe(androidx.test.uiautomator.Direction, float);
+ method public void swipe(androidx.test.uiautomator.Direction, float, int);
+ method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
}
public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
public class UiSelector {
ctor public UiSelector();
- method public androidx.test.uiautomator.UiSelector! checkable(boolean);
- method public androidx.test.uiautomator.UiSelector! checked(boolean);
- method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! className(String!);
- method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
- method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! clickable(boolean);
- method protected androidx.test.uiautomator.UiSelector! cloneSelector();
- method public androidx.test.uiautomator.UiSelector! description(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
- method public androidx.test.uiautomator.UiSelector! enabled(boolean);
- method public androidx.test.uiautomator.UiSelector! focusable(boolean);
- method public androidx.test.uiautomator.UiSelector! focused(boolean);
- method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! index(int);
- method public androidx.test.uiautomator.UiSelector! instance(int);
- method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
- method public androidx.test.uiautomator.UiSelector! packageName(String!);
- method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! resourceId(String!);
- method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
- method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
- method public androidx.test.uiautomator.UiSelector! selected(boolean);
- method public androidx.test.uiautomator.UiSelector! text(String!);
- method public androidx.test.uiautomator.UiSelector! textContains(String!);
- method public androidx.test.uiautomator.UiSelector! textMatches(String!);
- method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+ method public androidx.test.uiautomator.UiSelector checkable(boolean);
+ method public androidx.test.uiautomator.UiSelector checked(boolean);
+ method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector className(String);
+ method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+ method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector clickable(boolean);
+ method protected androidx.test.uiautomator.UiSelector cloneSelector();
+ method public androidx.test.uiautomator.UiSelector description(String);
+ method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+ method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+ method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+ method public androidx.test.uiautomator.UiSelector enabled(boolean);
+ method public androidx.test.uiautomator.UiSelector focusable(boolean);
+ method public androidx.test.uiautomator.UiSelector focused(boolean);
+ method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector index(int);
+ method public androidx.test.uiautomator.UiSelector instance(int);
+ method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+ method public androidx.test.uiautomator.UiSelector packageName(String);
+ method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector resourceId(String);
+ method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+ method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+ method public androidx.test.uiautomator.UiSelector selected(boolean);
+ method public androidx.test.uiautomator.UiSelector text(String);
+ method public androidx.test.uiautomator.UiSelector textContains(String);
+ method public androidx.test.uiautomator.UiSelector textMatches(String);
+ method public androidx.test.uiautomator.UiSelector textStartsWith(String);
}
public interface UiWatcher {
@@ -386,35 +386,34 @@
}
public class Until {
- ctor public Until();
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
- method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
}
}
diff --git a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
+++ b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
@@ -143,32 +143,32 @@
method public void clearLastTraversedText();
method public boolean click(int, int);
method public boolean drag(int, int, int, int, int);
- method @Deprecated public void dumpWindowHierarchy(String!);
- method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
- method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
- method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+ method @Deprecated public void dumpWindowHierarchy(String);
+ method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+ method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+ method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
method public void freezeRotation() throws android.os.RemoteException;
method @Deprecated public String! getCurrentActivityName();
method public String! getCurrentPackageName();
method public int getDisplayHeight();
method public int getDisplayRotation();
- method public android.graphics.Point! getDisplaySizeDp();
+ method public android.graphics.Point getDisplaySizeDp();
method public int getDisplayWidth();
- method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
- method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+ method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+ method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
method public String! getLastTraversedText();
method public String! getLauncherPackageName();
- method public String! getProductName();
+ method public String getProductName();
method public boolean hasAnyWatcherTriggered();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
- method public boolean hasWatcherTriggered(String!);
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
+ method public boolean hasWatcherTriggered(String?);
method public boolean isNaturalOrientation();
method public boolean isScreenOn() throws android.os.RemoteException;
method public boolean openNotification();
method public boolean openQuickSettings();
- method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+ method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
method public boolean pressBack();
method public boolean pressDPadCenter();
method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
method public boolean pressMenu();
method public boolean pressRecentApps() throws android.os.RemoteException;
method public boolean pressSearch();
- method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
- method public void removeWatcher(String!);
+ method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+ method public void removeWatcher(String?);
method public void resetWatcherTriggers();
method public void runWatchers();
method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
method public void setOrientationRight() throws android.os.RemoteException;
method public void sleep() throws android.os.RemoteException;
method public boolean swipe(int, int, int, int, int);
- method public boolean swipe(android.graphics.Point![]!, int);
- method public boolean takeScreenshot(java.io.File!);
- method public boolean takeScreenshot(java.io.File!, float, int);
+ method public boolean swipe(android.graphics.Point![], int);
+ method public boolean takeScreenshot(java.io.File);
+ method public boolean takeScreenshot(java.io.File, float, int);
method public void unfreezeRotation() throws android.os.RemoteException;
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
method public void waitForIdle();
method public void waitForIdle(long);
- method public boolean waitForWindowUpdate(String!, long);
+ method public boolean waitForWindowUpdate(String?, long);
method public void wakeUp() throws android.os.RemoteException;
}
@@ -212,20 +212,20 @@
method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean exists();
- method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
- method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+ method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public final androidx.test.uiautomator.UiSelector! getSelector();
- method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public final androidx.test.uiautomator.UiSelector getSelector();
+ method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
- method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+ method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
public class UiObject2 {
method public void clear();
method public void click();
- method public void click(android.graphics.Point!);
+ method public void click(android.graphics.Point);
method public void click(long);
- method public void click(android.graphics.Point!, long);
- method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
- method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
- method public void drag(android.graphics.Point!);
- method public void drag(android.graphics.Point!, int);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public boolean fling(androidx.test.uiautomator.Direction!);
- method public boolean fling(androidx.test.uiautomator.Direction!, int);
+ method public void click(android.graphics.Point, long);
+ method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+ method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+ method public void drag(android.graphics.Point);
+ method public void drag(android.graphics.Point, int);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+ method public boolean fling(androidx.test.uiautomator.Direction);
+ method public boolean fling(androidx.test.uiautomator.Direction, int);
method public String! getApplicationPackage();
method public int getChildCount();
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
method public String! getClassName();
method public String! getContentDescription();
method public int getDisplayId();
method public androidx.test.uiautomator.UiObject2! getParent();
method public String! getResourceName();
method public String! getText();
- method public android.graphics.Rect! getVisibleBounds();
- method public android.graphics.Point! getVisibleCenter();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+ method public android.graphics.Rect getVisibleBounds();
+ method public android.graphics.Point getVisibleCenter();
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -298,15 +298,15 @@
method public void pinchOpen(float);
method public void pinchOpen(float, int);
method public void recycle();
- method public boolean scroll(androidx.test.uiautomator.Direction!, float);
- method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
method public void setGestureMargin(int);
method public void setGestureMargins(int, int, int, int);
- method public void setText(String!);
- method public void swipe(androidx.test.uiautomator.Direction!, float);
- method public void swipe(androidx.test.uiautomator.Direction!, float, int);
- method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public void setText(String?);
+ method public void swipe(androidx.test.uiautomator.Direction, float);
+ method public void swipe(androidx.test.uiautomator.Direction, float, int);
+ method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
}
public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
public class UiSelector {
ctor public UiSelector();
- method public androidx.test.uiautomator.UiSelector! checkable(boolean);
- method public androidx.test.uiautomator.UiSelector! checked(boolean);
- method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! className(String!);
- method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
- method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! clickable(boolean);
- method protected androidx.test.uiautomator.UiSelector! cloneSelector();
- method public androidx.test.uiautomator.UiSelector! description(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
- method public androidx.test.uiautomator.UiSelector! enabled(boolean);
- method public androidx.test.uiautomator.UiSelector! focusable(boolean);
- method public androidx.test.uiautomator.UiSelector! focused(boolean);
- method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! index(int);
- method public androidx.test.uiautomator.UiSelector! instance(int);
- method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
- method public androidx.test.uiautomator.UiSelector! packageName(String!);
- method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! resourceId(String!);
- method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
- method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
- method public androidx.test.uiautomator.UiSelector! selected(boolean);
- method public androidx.test.uiautomator.UiSelector! text(String!);
- method public androidx.test.uiautomator.UiSelector! textContains(String!);
- method public androidx.test.uiautomator.UiSelector! textMatches(String!);
- method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+ method public androidx.test.uiautomator.UiSelector checkable(boolean);
+ method public androidx.test.uiautomator.UiSelector checked(boolean);
+ method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector className(String);
+ method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+ method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector clickable(boolean);
+ method protected androidx.test.uiautomator.UiSelector cloneSelector();
+ method public androidx.test.uiautomator.UiSelector description(String);
+ method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+ method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+ method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+ method public androidx.test.uiautomator.UiSelector enabled(boolean);
+ method public androidx.test.uiautomator.UiSelector focusable(boolean);
+ method public androidx.test.uiautomator.UiSelector focused(boolean);
+ method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector index(int);
+ method public androidx.test.uiautomator.UiSelector instance(int);
+ method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+ method public androidx.test.uiautomator.UiSelector packageName(String);
+ method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector resourceId(String);
+ method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+ method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+ method public androidx.test.uiautomator.UiSelector selected(boolean);
+ method public androidx.test.uiautomator.UiSelector text(String);
+ method public androidx.test.uiautomator.UiSelector textContains(String);
+ method public androidx.test.uiautomator.UiSelector textMatches(String);
+ method public androidx.test.uiautomator.UiSelector textStartsWith(String);
}
public interface UiWatcher {
@@ -386,35 +386,34 @@
}
public class Until {
- ctor public Until();
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
- method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
}
}
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -143,32 +143,32 @@
method public void clearLastTraversedText();
method public boolean click(int, int);
method public boolean drag(int, int, int, int, int);
- method @Deprecated public void dumpWindowHierarchy(String!);
- method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
- method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
- method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+ method @Deprecated public void dumpWindowHierarchy(String);
+ method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+ method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+ method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
method public void freezeRotation() throws android.os.RemoteException;
method @Deprecated public String! getCurrentActivityName();
method public String! getCurrentPackageName();
method public int getDisplayHeight();
method public int getDisplayRotation();
- method public android.graphics.Point! getDisplaySizeDp();
+ method public android.graphics.Point getDisplaySizeDp();
method public int getDisplayWidth();
- method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
- method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+ method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+ method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
method public String! getLastTraversedText();
method public String! getLauncherPackageName();
- method public String! getProductName();
+ method public String getProductName();
method public boolean hasAnyWatcherTriggered();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
- method public boolean hasWatcherTriggered(String!);
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
+ method public boolean hasWatcherTriggered(String?);
method public boolean isNaturalOrientation();
method public boolean isScreenOn() throws android.os.RemoteException;
method public boolean openNotification();
method public boolean openQuickSettings();
- method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+ method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
method public boolean pressBack();
method public boolean pressDPadCenter();
method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
method public boolean pressMenu();
method public boolean pressRecentApps() throws android.os.RemoteException;
method public boolean pressSearch();
- method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
- method public void removeWatcher(String!);
+ method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+ method public void removeWatcher(String?);
method public void resetWatcherTriggers();
method public void runWatchers();
method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
method public void setOrientationRight() throws android.os.RemoteException;
method public void sleep() throws android.os.RemoteException;
method public boolean swipe(int, int, int, int, int);
- method public boolean swipe(android.graphics.Point![]!, int);
- method public boolean takeScreenshot(java.io.File!);
- method public boolean takeScreenshot(java.io.File!, float, int);
+ method public boolean swipe(android.graphics.Point![], int);
+ method public boolean takeScreenshot(java.io.File);
+ method public boolean takeScreenshot(java.io.File, float, int);
method public void unfreezeRotation() throws android.os.RemoteException;
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
method public void waitForIdle();
method public void waitForIdle(long);
- method public boolean waitForWindowUpdate(String!, long);
+ method public boolean waitForWindowUpdate(String?, long);
method public void wakeUp() throws android.os.RemoteException;
}
@@ -212,20 +212,20 @@
method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean exists();
- method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
- method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+ method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public final androidx.test.uiautomator.UiSelector! getSelector();
- method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public final androidx.test.uiautomator.UiSelector getSelector();
+ method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
- method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+ method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
- method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+ method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
public class UiObject2 {
method public void clear();
method public void click();
- method public void click(android.graphics.Point!);
+ method public void click(android.graphics.Point);
method public void click(long);
- method public void click(android.graphics.Point!, long);
- method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
- method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
- method public void drag(android.graphics.Point!);
- method public void drag(android.graphics.Point!, int);
- method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public boolean fling(androidx.test.uiautomator.Direction!);
- method public boolean fling(androidx.test.uiautomator.Direction!, int);
+ method public void click(android.graphics.Point, long);
+ method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+ method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+ method public void drag(android.graphics.Point);
+ method public void drag(android.graphics.Point, int);
+ method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+ method public boolean fling(androidx.test.uiautomator.Direction);
+ method public boolean fling(androidx.test.uiautomator.Direction, int);
method public String! getApplicationPackage();
method public int getChildCount();
- method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+ method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
method public String! getClassName();
method public String! getContentDescription();
method public int getDisplayId();
method public androidx.test.uiautomator.UiObject2! getParent();
method public String! getResourceName();
method public String! getText();
- method public android.graphics.Rect! getVisibleBounds();
- method public android.graphics.Point! getVisibleCenter();
- method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+ method public android.graphics.Rect getVisibleBounds();
+ method public android.graphics.Point getVisibleCenter();
+ method public boolean hasObject(androidx.test.uiautomator.BySelector);
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -298,15 +298,15 @@
method public void pinchOpen(float);
method public void pinchOpen(float, int);
method public void recycle();
- method public boolean scroll(androidx.test.uiautomator.Direction!, float);
- method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float);
+ method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
method public void setGestureMargin(int);
method public void setGestureMargins(int, int, int, int);
- method public void setText(String!);
- method public void swipe(androidx.test.uiautomator.Direction!, float);
- method public void swipe(androidx.test.uiautomator.Direction!, float, int);
- method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
- method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+ method public void setText(String?);
+ method public void swipe(androidx.test.uiautomator.Direction, float);
+ method public void swipe(androidx.test.uiautomator.Direction, float, int);
+ method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+ method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
}
public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
public class UiSelector {
ctor public UiSelector();
- method public androidx.test.uiautomator.UiSelector! checkable(boolean);
- method public androidx.test.uiautomator.UiSelector! checked(boolean);
- method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! className(String!);
- method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
- method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! clickable(boolean);
- method protected androidx.test.uiautomator.UiSelector! cloneSelector();
- method public androidx.test.uiautomator.UiSelector! description(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
- method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
- method public androidx.test.uiautomator.UiSelector! enabled(boolean);
- method public androidx.test.uiautomator.UiSelector! focusable(boolean);
- method public androidx.test.uiautomator.UiSelector! focused(boolean);
- method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
- method public androidx.test.uiautomator.UiSelector! index(int);
- method public androidx.test.uiautomator.UiSelector! instance(int);
- method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
- method public androidx.test.uiautomator.UiSelector! packageName(String!);
- method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
- method public androidx.test.uiautomator.UiSelector! resourceId(String!);
- method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
- method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
- method public androidx.test.uiautomator.UiSelector! selected(boolean);
- method public androidx.test.uiautomator.UiSelector! text(String!);
- method public androidx.test.uiautomator.UiSelector! textContains(String!);
- method public androidx.test.uiautomator.UiSelector! textMatches(String!);
- method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+ method public androidx.test.uiautomator.UiSelector checkable(boolean);
+ method public androidx.test.uiautomator.UiSelector checked(boolean);
+ method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector className(String);
+ method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+ method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector clickable(boolean);
+ method protected androidx.test.uiautomator.UiSelector cloneSelector();
+ method public androidx.test.uiautomator.UiSelector description(String);
+ method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+ method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+ method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+ method public androidx.test.uiautomator.UiSelector enabled(boolean);
+ method public androidx.test.uiautomator.UiSelector focusable(boolean);
+ method public androidx.test.uiautomator.UiSelector focused(boolean);
+ method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+ method public androidx.test.uiautomator.UiSelector index(int);
+ method public androidx.test.uiautomator.UiSelector instance(int);
+ method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+ method public androidx.test.uiautomator.UiSelector packageName(String);
+ method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+ method public androidx.test.uiautomator.UiSelector resourceId(String);
+ method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+ method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+ method public androidx.test.uiautomator.UiSelector selected(boolean);
+ method public androidx.test.uiautomator.UiSelector text(String);
+ method public androidx.test.uiautomator.UiSelector textContains(String);
+ method public androidx.test.uiautomator.UiSelector textMatches(String);
+ method public androidx.test.uiautomator.UiSelector textStartsWith(String);
}
public interface UiWatcher {
@@ -386,35 +386,34 @@
}
public class Until {
- ctor public Until();
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
- method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
- method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
- method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+ method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+ method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
}
}
diff --git a/test/uiautomator/uiautomator/lint-baseline.xml b/test/uiautomator/uiautomator/lint-baseline.xml
index 89c3bf6..4207262 100644
--- a/test/uiautomator/uiautomator/lint-baseline.xml
+++ b/test/uiautomator/uiautomator/lint-baseline.xml
@@ -11,19 +11,10 @@
</issue>
<issue
- id="PrivateConstructorForUtilityClass"
- message="Utility class is missing private constructor"
- errorLine1="public class Until {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
id="LambdaLast"
message="Functional interface parameters (such as parameter 1, "action", in androidx.test.uiautomator.UiDevice.performActionAndWait) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
- errorLine1=" public <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~">
+ errorLine1=" @NonNull EventCondition<R> condition, long timeout) {"
+ errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
</issue>
@@ -49,357 +40,6 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject findObject(UiSelector selector) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject findObject(UiSelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean hasObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject2 findObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject2 findObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public List<UiObject2> findObjects(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public List<UiObject2> findObjects(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R wait(SearchCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiDevice getInstance(Instrumentation instrumentation) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiDevice getInstance(Instrumentation instrumentation) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Point getDisplaySizeDp() {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getProductName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getLastTraversedText() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean swipe(Point[] segments, int segmentSteps) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getCurrentPackageName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void registerWatcher(String name, UiWatcher watcher) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void registerWatcher(String name, UiWatcher watcher) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void removeWatcher(String name) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean hasWatcherTriggered(String watcherName) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void dumpWindowHierarchy(File dest) throws IOException {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void dumpWindowHierarchy(OutputStream out) throws IOException {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean waitForWindowUpdate(final String packageName, long timeout) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean takeScreenshot(File storePath) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean takeScreenshot(File storePath, float scale, int quality) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getLauncherPackageName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String executeShellCommand(String cmd) throws IOException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String executeShellCommand(String cmd) throws IOException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public final UiSelector getSelector() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getText() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getClassName() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getContentDescription() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public void legacySetText(String text) throws UiObjectNotFoundException {"
errorLine2=" ~~~~~~">
<location
@@ -409,1189 +49,10 @@
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean setText(String text) throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getPackageName() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Rect getVisibleBounds() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Rect getBounds() throws UiObjectNotFoundException {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" Point endPoint2, int steps) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean performMultiPointerGesture(PointerCoords[] ...touches) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R wait(UiObject2Condition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R wait(SearchCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject2 getParent() {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public List<UiObject2> getChildren() {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean hasObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject2 findObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiObject2 findObject(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public List<UiObject2> findObjects(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public List<UiObject2> findObjects(BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Rect getVisibleBounds() {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public Point getVisibleCenter() {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getClassName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getContentDescription() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getApplicationPackage() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getResourceName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public String getText() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void click(Point point) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void click(Point point, long duration) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R clickAndWait(EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R clickAndWait(Point point, EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <R> R clickAndWait(Point point, EventCondition<R> condition, long timeout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void drag(Point dest) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void drag(Point dest, int speed) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void swipe(Direction direction, float percent) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void swipe(Direction direction, float percent, int speed) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean scroll(final Direction direction, final float percent) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean scroll(Direction direction, float percent, final int speed) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean fling(final Direction direction) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public boolean fling(final Direction direction, final int speed) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public void legacySetText(String text) {"
errorLine2=" ~~~~~~">
<location
file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
</issue>
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public void setText(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" protected UiSelector cloneSelector() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector text(String text) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector text(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textStartsWith(String text) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textStartsWith(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textContains(String text) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector textContains(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector className(String className) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector className(String className) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector classNameMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector classNameMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <T> UiSelector className(Class<T> type) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public <T> UiSelector className(Class<T> type) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector description(String desc) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector description(String desc) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionStartsWith(String desc) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionStartsWith(String desc) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionContains(String desc) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector descriptionContains(String desc) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector resourceId(String id) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector resourceId(String id) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector resourceIdMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector resourceIdMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector index(final int index) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector instance(final int instance) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector enabled(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector focused(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector focusable(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector scrollable(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector selected(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector checked(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector clickable(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector checkable(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector longClickable(boolean val) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector childSelector(UiSelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector childSelector(UiSelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector fromParent(UiSelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector fromParent(UiSelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector packageName(String name) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector packageName(String name) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector packageNameMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public UiSelector packageNameMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<Boolean> gone(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<Boolean> gone(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<Boolean> hasObject(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<Boolean> hasObject(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<UiObject2> findObject(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<UiObject2> findObject(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<List<UiObject2>> findObjects(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static SearchCondition<List<UiObject2>> findObjects(final BySelector selector) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> checkable(final boolean isCheckable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> checked(final boolean isChecked) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> clickable(final boolean isClickable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> enabled(final boolean isEnabled) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> focusable(final boolean isFocusable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> focused(final boolean isFocused) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> longClickable(final boolean isLongClickable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> scrollable(final boolean isScrollable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> selected(final boolean isSelected) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descMatches(final Pattern regex) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descMatches(final Pattern regex) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descEquals(String contentDescription) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descEquals(String contentDescription) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descContains(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descContains(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descStartsWith(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descStartsWith(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descEndsWith(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> descEndsWith(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textMatches(final Pattern regex) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textMatches(final Pattern regex) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textMatches(String regex) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textMatches(String regex) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textNotEquals(final String text) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textNotEquals(final String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textEquals(String text) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textEquals(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textContains(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textContains(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textStartsWith(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textStartsWith(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textEndsWith(String substring) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static UiObject2Condition<Boolean> textEndsWith(String substring) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static EventCondition<Boolean> newWindow() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static EventCondition<Boolean> scrollFinished(final Direction direction) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
- errorLine1=" public static EventCondition<Boolean> scrollFinished(final Direction direction) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/test/uiautomator/Until.java"/>
- </issue>
-
</issues>
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
index 447a0f7..e796744 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
@@ -16,17 +16,20 @@
package androidx.test.uiautomator;
+import androidx.annotation.NonNull;
+
import java.util.List;
/** The Searchable interface represents an object that can be searched for matching UI elements. */
interface Searchable {
/** Returns whether there is a match for the given {@code selector} criteria. */
- public boolean hasObject(BySelector selector);
+ boolean hasObject(@NonNull BySelector selector);
/** Returns the first object to match the {@code selector} criteria. */
- public UiObject2 findObject(BySelector selector);
+ UiObject2 findObject(@NonNull BySelector selector);
/** Returns all objects that match the {@code selector} criteria. */
- public List<UiObject2> findObjects(BySelector selector);
+ @NonNull
+ List<UiObject2> findObjects(@NonNull BySelector selector);
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index 5f43d03..becd801 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -17,6 +17,7 @@
package androidx.test.uiautomator;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.SuppressLint;
import android.app.Instrumentation;
import android.app.Service;
import android.app.UiAutomation;
@@ -44,6 +45,8 @@
import android.view.accessibility.AccessibilityWindowInfo;
import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.io.BufferedOutputStream;
@@ -125,13 +128,14 @@
* @param selector
* @return UiObject object
*/
- public UiObject findObject(UiSelector selector) {
+ @NonNull
+ public UiObject findObject(@NonNull UiSelector selector) {
return new UiObject(this, selector);
}
/** Returns whether there is a match for the given {@code selector} criteria. */
@Override
- public boolean hasObject(BySelector selector) {
+ public boolean hasObject(@NonNull BySelector selector) {
AccessibilityNodeInfo node = ByMatcher.findMatch(this, selector, getWindowRoots());
if (node != null) {
node.recycle();
@@ -145,14 +149,16 @@
* or null if no matching objects are found.
*/
@Override
- public UiObject2 findObject(BySelector selector) {
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
+ public UiObject2 findObject(@NonNull BySelector selector) {
AccessibilityNodeInfo node = ByMatcher.findMatch(this, selector, getWindowRoots());
return node != null ? new UiObject2(this, selector, node) : null;
}
/** Returns all objects that match the {@code selector} criteria. */
@Override
- public List<UiObject2> findObjects(BySelector selector) {
+ @NonNull
+ public List<UiObject2> findObjects(@NonNull BySelector selector) {
List<UiObject2> ret = new ArrayList<UiObject2>();
for (AccessibilityNodeInfo node : ByMatcher.findMatches(this, selector, getWindowRoots())) {
ret.add(new UiObject2(this, selector, node));
@@ -171,7 +177,7 @@
* was not met before the {@code timeout}.
*/
@SuppressWarnings("TypeNameShadowing")
- public <R> R wait(SearchCondition<R> condition, long timeout) {
+ public <R> R wait(@NonNull SearchCondition<R> condition, long timeout) {
return mWaitMixin.wait(condition, timeout);
}
@@ -184,8 +190,8 @@
* @return The final result returned by the condition.
*/
@SuppressWarnings("TypeNameShadowing")
- public <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) {
-
+ public <R> R performActionAndWait(@NonNull Runnable action,
+ @NonNull EventCondition<R> condition, long timeout) {
AccessibilityEvent event = null;
try {
event = getUiAutomation().executeAndWaitForEvent(
@@ -246,6 +252,7 @@
* @since API Level 16
*/
@Deprecated
+ @NonNull
public static UiDevice getInstance() {
if (sInstance == null) {
throw new IllegalStateException("UiDevice singleton not initialized");
@@ -258,7 +265,8 @@
*
* @return UiDevice instance
*/
- public static UiDevice getInstance(Instrumentation instrumentation) {
+ @NonNull
+ public static UiDevice getInstance(@NonNull Instrumentation instrumentation) {
if (sInstance == null) {
sInstance = new UiDevice(instrumentation);
}
@@ -273,6 +281,7 @@
*
* @return a Point containing the display size in dp
*/
+ @NonNull
public Point getDisplaySizeDp() {
Tracer.trace();
Display display = getDefaultDisplay();
@@ -296,6 +305,7 @@
* @return product name of the device
* @since API Level 17
*/
+ @NonNull
public String getProductName() {
Tracer.trace();
return Build.PRODUCT;
@@ -316,6 +326,7 @@
* @return text of the last traversal event, else return an empty string
* @since API Level 16
*/
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getLastTraversedText() {
Tracer.trace();
return getQueryController().getLastTraversedText();
@@ -608,7 +619,7 @@
* @return true on success
* @since API Level 16
*/
- public boolean swipe(Point[] segments, int segmentSteps) {
+ public boolean swipe(@NonNull Point[] segments, int segmentSteps) {
Tracer.trace(segments, segmentSteps);
return getInteractionController().swipe(segments, segmentSteps);
}
@@ -640,6 +651,7 @@
* @since API Level 16
*/
@Deprecated
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getCurrentActivityName() {
Tracer.trace();
return getQueryController().getCurrentActivityName();
@@ -650,6 +662,7 @@
* @return String name of package
* @since API Level 16
*/
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getCurrentPackageName() {
Tracer.trace();
return getQueryController().getCurrentPackageName();
@@ -663,7 +676,7 @@
* @param watcher {@link UiWatcher}
* @since API Level 16
*/
- public void registerWatcher(String name, UiWatcher watcher) {
+ public void registerWatcher(@Nullable String name, @Nullable UiWatcher watcher) {
Tracer.trace(name, watcher);
if (mInWatcherContext) {
throw new IllegalStateException("Cannot register new watcher from within another");
@@ -678,7 +691,7 @@
* @param name used to register the UiWatcher
* @since API Level 16
*/
- public void removeWatcher(String name) {
+ public void removeWatcher(@Nullable String name) {
Tracer.trace(name);
if (mInWatcherContext) {
throw new IllegalStateException("Cannot remove a watcher from within another");
@@ -737,7 +750,7 @@
* @return true if triggered else false
* @since API Level 16
*/
- public boolean hasWatcherTriggered(String watcherName) {
+ public boolean hasWatcherTriggered(@Nullable String watcherName) {
Tracer.trace(watcherName);
return mWatchersTriggers.contains(watcherName);
}
@@ -908,7 +921,7 @@
* {@link UiDevice#dumpWindowHierarchy(OutputStream)} instead.
*/
@Deprecated
- public void dumpWindowHierarchy(String fileName) {
+ public void dumpWindowHierarchy(@NonNull String fileName) {
Tracer.trace(fileName);
File dumpFile = new File(fileName);
@@ -928,7 +941,7 @@
* @param dest The file in which to store the window hierarchy information.
* @throws IOException
*/
- public void dumpWindowHierarchy(File dest) throws IOException {
+ public void dumpWindowHierarchy(@NonNull File dest) throws IOException {
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(dest))) {
dumpWindowHierarchy(stream);
}
@@ -940,7 +953,7 @@
* @param out The output stream that the window hierarchy information is written to.
* @throws IOException
*/
- public void dumpWindowHierarchy(OutputStream out) throws IOException {
+ public void dumpWindowHierarchy(@NonNull OutputStream out) throws IOException {
AccessibilityNodeInfoDumper.dumpWindowHierarchy(this, out);
}
@@ -958,7 +971,7 @@
* window does not have the specified package name
* @since API Level 16
*/
- public boolean waitForWindowUpdate(final String packageName, long timeout) {
+ public boolean waitForWindowUpdate(@Nullable String packageName, long timeout) {
Tracer.trace(packageName, timeout);
if (packageName != null) {
if (!packageName.equals(getCurrentPackageName())) {
@@ -1001,7 +1014,7 @@
* @return true if screen shot is created successfully, false otherwise
* @since API Level 17
*/
- public boolean takeScreenshot(File storePath) {
+ public boolean takeScreenshot(@NonNull File storePath) {
Tracer.trace(storePath);
return takeScreenshot(storePath, 1.0f, 90);
}
@@ -1017,7 +1030,7 @@
* @return true if screen shot is created successfully, false otherwise
* @since API Level 17
*/
- public boolean takeScreenshot(File storePath, float scale, int quality) {
+ public boolean takeScreenshot(@NonNull File storePath, float scale, int quality) {
Tracer.trace(storePath, scale, quality);
Bitmap screenshot = getUiAutomation().takeScreenshot();
if (screenshot == null) {
@@ -1043,6 +1056,7 @@
*
* @return package name of the default launcher
*/
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getLauncherPackageName() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
@@ -1064,7 +1078,8 @@
* @hide
*/
@RequiresApi(21)
- public String executeShellCommand(String cmd) throws IOException {
+ @NonNull
+ public String executeShellCommand(@NonNull String cmd) throws IOException {
try (ParcelFileDescriptor pfd = Api21Impl.executeShellCommand(getUiAutomation(), cmd);
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
byte[] buf = new byte[512];
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
index 22c1a0f..fdf7cb3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
@@ -26,6 +26,9 @@
import android.view.MotionEvent.PointerCoords;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* A UiObject is a representation of a view. It is not in any way directly bound to a
* view as an object reference. A UiObject contains information to help it
@@ -102,6 +105,7 @@
* @return {@link UiSelector}
* @since API Level 16
*/
+ @NonNull
public final UiSelector getSelector() {
Tracer.trace();
if (mUiSelector == null) {
@@ -141,7 +145,8 @@
* @return a new UiObject representing the child view
* @since API Level 16
*/
- public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
+ @NonNull
+ public UiObject getChild(@NonNull UiSelector selector) throws UiObjectNotFoundException {
Tracer.trace(selector);
return new UiObject(getSelector().childSelector(selector));
}
@@ -155,7 +160,8 @@
* @throws UiObjectNotFoundException
* @since API Level 16
*/
- public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
+ @NonNull
+ public UiObject getFromParent(@NonNull UiSelector selector) throws UiObjectNotFoundException {
Tracer.trace(selector);
return new UiObject(getSelector().fromParent(selector));
}
@@ -184,6 +190,7 @@
* @return AccessibilityNodeInfo if found else null
* @since API Level 16
*/
+ @Nullable
protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
AccessibilityNodeInfo node = null;
long startMills = SystemClock.uptimeMillis();
@@ -216,7 +223,7 @@
* @throws UiObjectNotFoundException
* @since API Level 18
*/
- public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {
+ public boolean dragTo(@NonNull UiObject destObj, int steps) throws UiObjectNotFoundException {
Rect srcRect = getVisibleBounds();
Rect dstRect = destObj.getVisibleBounds();
return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(),
@@ -560,6 +567,7 @@
* @throws UiObjectNotFoundException if no match could be found
* @since API Level 16
*/
+ @NonNull
public String getText() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -578,6 +586,7 @@
* @throws UiObjectNotFoundException if no match was found
* @since API Level 18
*/
+ @NonNull
public String getClassName() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -596,6 +605,7 @@
* @throws UiObjectNotFoundException
* @since API Level 16
*/
+ @NonNull
public String getContentDescription() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -655,7 +665,7 @@
* @throws UiObjectNotFoundException
* @since API Level 16
*/
- public boolean setText(String text) throws UiObjectNotFoundException {
+ public boolean setText(@Nullable String text) throws UiObjectNotFoundException {
// per framework convention, setText with null means clearing it
if (text == null) {
text = "";
@@ -870,6 +880,7 @@
* @throws UiObjectNotFoundException
* @since API Level 16
*/
+ @NonNull
public String getPackageName() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -891,6 +902,7 @@
*
* @since API Level 17
*/
+ @NonNull
public Rect getVisibleBounds() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -907,6 +919,7 @@
* @throws UiObjectNotFoundException
* @since API Level 16
*/
+ @NonNull
public Rect getBounds() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -1080,8 +1093,8 @@
* <code>false</code> otherwise
* @since API Level 18
*/
- public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,
- Point endPoint2, int steps) {
+ public boolean performTwoPointerGesture(@NonNull Point startPoint1, @NonNull Point startPoint2,
+ @NonNull Point endPoint1, @NonNull Point endPoint2, int steps) {
// avoid a divide by zero
if(steps == 0)
@@ -1164,7 +1177,7 @@
* <code>false</code> otherwise
* @since API Level 18
*/
- public boolean performMultiPointerGesture(PointerCoords[] ...touches) {
+ public boolean performMultiPointerGesture(@NonNull PointerCoords[]... touches) {
return getInteractionController().performMultiPointerGesture(touches);
}
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 19ede49..cafe068 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -16,6 +16,7 @@
package androidx.test.uiautomator;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.graphics.Point;
import android.graphics.Rect;
@@ -32,6 +33,8 @@
import android.view.accessibility.AccessibilityWindowInfo;
import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
@@ -154,7 +157,7 @@
* was not met before the {@code timeout}.
*/
@SuppressWarnings("TypeNameShadowing")
- public <R> R wait(UiObject2Condition<R> condition, long timeout) {
+ public <R> R wait(@NonNull UiObject2Condition<R> condition, long timeout) {
return mWaitMixin.wait(condition, timeout);
}
@@ -167,13 +170,14 @@
* was not met before the {@code timeout}.
*/
@SuppressWarnings("TypeNameShadowing")
- public <R> R wait(SearchCondition<R> condition, long timeout) {
+ public <R> R wait(@NonNull SearchCondition<R> condition, long timeout) {
return mWaitMixin.wait(condition, timeout);
}
// Search functions
/** Returns this object's parent, or null if it has no parent. */
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public UiObject2 getParent() {
AccessibilityNodeInfo parent = getAccessibilityNodeInfo().getParent();
return parent != null ? new UiObject2(getDevice(), mSelector, parent) : null;
@@ -185,13 +189,14 @@
}
/** Returns a collection of the child elements directly under this object. */
+ @NonNull
public List<UiObject2> getChildren() {
return findObjects(By.depth(1));
}
/** Returns whether there is a match for the given criteria under this object. */
@Override
- public boolean hasObject(BySelector selector) {
+ public boolean hasObject(@NonNull BySelector selector) {
AccessibilityNodeInfo node =
ByMatcher.findMatch(getDevice(), selector, getAccessibilityNodeInfo());
if (node != null) {
@@ -206,7 +211,8 @@
* or null if no matching objects are found.
*/
@Override
- public UiObject2 findObject(BySelector selector) {
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
+ public UiObject2 findObject(@NonNull BySelector selector) {
AccessibilityNodeInfo node =
ByMatcher.findMatch(getDevice(), selector, getAccessibilityNodeInfo());
return node != null ? new UiObject2(getDevice(), selector, node) : null;
@@ -214,7 +220,8 @@
/** Searches all elements under this object and returns all objects that match the criteria. */
@Override
- public List<UiObject2> findObjects(BySelector selector) {
+ @NonNull
+ public List<UiObject2> findObjects(@NonNull BySelector selector) {
List<UiObject2> ret = new ArrayList<UiObject2>();
for (AccessibilityNodeInfo node :
ByMatcher.findMatches(getDevice(), selector, getAccessibilityNodeInfo())) {
@@ -239,6 +246,7 @@
}
/** Returns the visible bounds of this object in screen coordinates. */
+ @NonNull
public Rect getVisibleBounds() {
return getVisibleBounds(getAccessibilityNodeInfo());
}
@@ -323,6 +331,7 @@
}
/** Returns a point in the center of the visible bounds of this object. */
+ @NonNull
public Point getVisibleCenter() {
Rect bounds = getVisibleBounds();
return new Point(bounds.centerX(), bounds.centerY());
@@ -332,30 +341,35 @@
* Returns the class name of the underlying {@link android.view.View} represented by this
* object.
*/
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getClassName() {
CharSequence chars = getAccessibilityNodeInfo().getClassName();
return chars != null ? chars.toString() : null;
}
/** Returns the content description for this object. */
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getContentDescription() {
CharSequence chars = getAccessibilityNodeInfo().getContentDescription();
return chars != null ? chars.toString() : null;
}
/** Returns the package name of the app that this object belongs to. */
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getApplicationPackage() {
CharSequence chars = getAccessibilityNodeInfo().getPackageName();
return chars != null ? chars.toString() : null;
}
/** Returns the fully qualified resource name for this object's id. */
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getResourceName() {
CharSequence chars = getAccessibilityNodeInfo().getViewIdResourceName();
return chars != null ? chars.toString() : null;
}
/** Returns the text value for this object. */
+ @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getText() {
CharSequence chars = getAccessibilityNodeInfo().getText();
return chars != null ? chars.toString() : null;
@@ -426,7 +440,7 @@
* @param point The point to click. Clipped to the visible bounds of this object with gesture
* margins removed.
*/
- public void click(Point point) {
+ public void click(@NonNull Point point) {
clipToGestureBounds(point);
Log.v(TAG, String.format("click(point=%s)", point));
mGestureController.performGesture(mGestures.click(point, getDisplayId()));
@@ -448,7 +462,7 @@
* margins removed.
* @param duration The duration in milliseconds to press {@code point}.
*/
- public void click(Point point, long duration) {
+ public void click(@NonNull Point point, long duration) {
clipToGestureBounds(point);
Log.v(TAG, String.format("click(point=%s,duration=%d)", point, duration));
mGestureController.performGesture(mGestures.click(point, duration, getDisplayId()));
@@ -456,7 +470,7 @@
/** Clicks on this object, and waits for the given condition to become true. */
@SuppressWarnings("TypeNameShadowing")
- public <R> R clickAndWait(EventCondition<R> condition, long timeout) {
+ public <R> R clickAndWait(@NonNull EventCondition<R> condition, long timeout) {
Log.v(TAG, String.format("clickAndWait(center=%s,timeout=%d)",
getVisibleCenter(), timeout));
return mGestureController.performGestureAndWait(condition, timeout,
@@ -473,7 +487,8 @@
* @param timeout The duration in milliseconds waiting for {@code condition} before timed out.
*/
@SuppressWarnings("TypeNameShadowing")
- public <R> R clickAndWait(Point point, EventCondition<R> condition, long timeout) {
+ public <R> R clickAndWait(@NonNull Point point, @NonNull EventCondition<R> condition,
+ long timeout) {
clipToGestureBounds(point);
Log.v(TAG, String.format("clickAndWait(point=%s,timeout=%d)", point, timeout));
return mGestureController.performGestureAndWait(condition, timeout,
@@ -485,7 +500,7 @@
*
* @param dest The end point that this object should be dragged to.
*/
- public void drag(Point dest) {
+ public void drag(@NonNull Point dest) {
drag(dest, (int)(DEFAULT_DRAG_SPEED * mDisplayDensity));
}
@@ -495,7 +510,7 @@
* @param dest The end point that this object should be dragged to.
* @param speed The speed at which to perform this gesture in pixels per second.
*/
- public void drag(Point dest, int speed) {
+ public void drag(@NonNull Point dest, int speed) {
if (speed < 0) {
throw new IllegalArgumentException("Speed cannot be negative");
}
@@ -576,7 +591,7 @@
* @param direction The direction in which to swipe.
* @param percent The length of the swipe as a percentage of this object's size.
*/
- public void swipe(Direction direction, float percent) {
+ public void swipe(@NonNull Direction direction, float percent) {
swipe(direction, percent, (int)(DEFAULT_SWIPE_SPEED * mDisplayDensity));
}
@@ -587,7 +602,7 @@
* @param percent The length of the swipe as a percentage of this object's size.
* @param speed The speed at which to perform this gesture in pixels per second.
*/
- public void swipe(Direction direction, float percent, int speed) {
+ public void swipe(@NonNull Direction direction, float percent, int speed) {
if (percent < 0.0f || percent > 1.0f) {
throw new IllegalArgumentException("Percent must be between 0.0f and 1.0f");
}
@@ -608,7 +623,7 @@
* @param percent The distance to scroll as a percentage of this object's visible size.
* @return Whether the object can still scroll in the given direction.
*/
- public boolean scroll(final Direction direction, final float percent) {
+ public boolean scroll(@NonNull Direction direction, final float percent) {
return scroll(direction, percent, (int)(DEFAULT_SCROLL_SPEED * mDisplayDensity));
}
@@ -620,7 +635,7 @@
* @param speed The speed at which to perform this gesture in pixels per second.
* @return Whether the object can still scroll in the given direction.
*/
- public boolean scroll(Direction direction, float percent, final int speed) {
+ public boolean scroll(@NonNull Direction direction, float percent, final int speed) {
if (percent < 0.0f) {
throw new IllegalArgumentException("Percent must be greater than 0.0f");
}
@@ -656,7 +671,7 @@
* @param direction The direction in which to fling.
* @return Whether the object can still scroll in the given direction.
*/
- public boolean fling(final Direction direction) {
+ public boolean fling(@NonNull Direction direction) {
return fling(direction, (int)(DEFAULT_FLING_SPEED * mDisplayDensity));
}
@@ -667,7 +682,7 @@
* @param speed The speed at which to perform this gesture in pixels per second.
* @return Whether the object can still scroll in the given direction.
*/
- public boolean fling(final Direction direction, final int speed) {
+ public boolean fling(@NonNull Direction direction, final int speed) {
ViewConfiguration vc = ViewConfiguration.get(getDevice().getUiContext());
if (speed < vc.getScaledMinimumFlingVelocity()) {
throw new IllegalArgumentException("Speed is less than the minimum fling velocity");
@@ -720,7 +735,7 @@
}
/** Sets the text content if this object is an editable field. */
- public void setText(String text) {
+ public void setText(@Nullable String text) {
AccessibilityNodeInfo node = getAccessibilityNodeInfo();
// Per framework convention, setText(null) means clearing it
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
index 8987c07..a09266a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
@@ -19,6 +19,8 @@
import android.util.SparseArray;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.annotation.NonNull;
+
import java.util.regex.Pattern;
/**
@@ -77,6 +79,7 @@
/**
* @since API Level 17
*/
+ @NonNull
protected UiSelector cloneSelector() {
UiSelector ret = new UiSelector();
ret.mSelectorAttributes = mSelectorAttributes.clone();
@@ -112,7 +115,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector text(String text) {
+ @NonNull
+ public UiSelector text(@NonNull String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -130,7 +134,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
- public UiSelector textMatches(String regex) {
+ @NonNull
+ public UiSelector textMatches(@NonNull String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
@@ -147,7 +152,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector textStartsWith(String text) {
+ @NonNull
+ public UiSelector textStartsWith(@NonNull String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -164,7 +170,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector textContains(String text) {
+ @NonNull
+ public UiSelector textContains(@NonNull String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -179,7 +186,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector className(String className) {
+ @NonNull
+ public UiSelector className(@NonNull String className) {
if (className == null) {
throw new IllegalArgumentException("className cannot be null");
}
@@ -194,7 +202,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
- public UiSelector classNameMatches(String regex) {
+ @NonNull
+ public UiSelector classNameMatches(@NonNull String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
@@ -209,7 +218,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
- public <T> UiSelector className(Class<T> type) {
+ @NonNull
+ public <T> UiSelector className(@NonNull Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("type cannot be null");
}
@@ -233,7 +243,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector description(String desc) {
+ @NonNull
+ public UiSelector description(@NonNull String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
@@ -255,7 +266,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
- public UiSelector descriptionMatches(String regex) {
+ @NonNull
+ public UiSelector descriptionMatches(@NonNull String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
@@ -279,7 +291,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector descriptionStartsWith(String desc) {
+ @NonNull
+ public UiSelector descriptionStartsWith(@NonNull String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
@@ -303,7 +316,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector descriptionContains(String desc) {
+ @NonNull
+ public UiSelector descriptionContains(@NonNull String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
@@ -317,7 +331,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
- public UiSelector resourceId(String id) {
+ @NonNull
+ public UiSelector resourceId(@NonNull String id) {
if (id == null) {
throw new IllegalArgumentException("id cannot be null");
}
@@ -332,7 +347,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
- public UiSelector resourceIdMatches(String regex) {
+ @NonNull
+ public UiSelector resourceIdMatches(@NonNull String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
@@ -353,6 +369,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector index(final int index) {
return buildSelector(SELECTOR_INDEX, index);
}
@@ -379,6 +396,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector instance(final int instance) {
return buildSelector(SELECTOR_INSTANCE, instance);
}
@@ -398,6 +416,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector enabled(boolean val) {
return buildSelector(SELECTOR_ENABLED, val);
}
@@ -417,6 +436,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector focused(boolean val) {
return buildSelector(SELECTOR_FOCUSED, val);
}
@@ -436,6 +456,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector focusable(boolean val) {
return buildSelector(SELECTOR_FOCUSABLE, val);
}
@@ -455,6 +476,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector scrollable(boolean val) {
return buildSelector(SELECTOR_SCROLLABLE, val);
}
@@ -475,6 +497,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector selected(boolean val) {
return buildSelector(SELECTOR_SELECTED, val);
}
@@ -495,6 +518,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector checked(boolean val) {
return buildSelector(SELECTOR_CHECKED, val);
}
@@ -514,6 +538,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
+ @NonNull
public UiSelector clickable(boolean val) {
return buildSelector(SELECTOR_CLICKABLE, val);
}
@@ -533,6 +558,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
+ @NonNull
public UiSelector checkable(boolean val) {
return buildSelector(SELECTOR_CHECKABLE, val);
}
@@ -552,6 +578,7 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
+ @NonNull
public UiSelector longClickable(boolean val) {
return buildSelector(SELECTOR_LONG_CLICKABLE, val);
}
@@ -566,7 +593,8 @@
* @return UiSelector with this added search criterion
* @since API Level 16
*/
- public UiSelector childSelector(UiSelector selector) {
+ @NonNull
+ public UiSelector childSelector(@NonNull UiSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector cannot be null");
}
@@ -592,7 +620,8 @@
* @return UiSelector with this added search criterion
* @since API Level 16
*/
- public UiSelector fromParent(UiSelector selector) {
+ @NonNull
+ public UiSelector fromParent(@NonNull UiSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector cannot be null");
}
@@ -607,7 +636,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
- public UiSelector packageName(String name) {
+ @NonNull
+ public UiSelector packageName(@NonNull String name) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
@@ -622,7 +652,8 @@
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
- public UiSelector packageNameMatches(String regex) {
+ @NonNull
+ public UiSelector packageNameMatches(@NonNull String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
index b1eaebf..1a0d622 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
@@ -18,6 +18,8 @@
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.NonNull;
+
import java.util.List;
import java.util.regex.Pattern;
@@ -26,13 +28,17 @@
*/
public class Until {
+ private Until() {
+ }
+
// Search conditions
/**
* Returns a {@link SearchCondition} that is satisfied when no elements matching the selector
* can be found.
*/
- public static SearchCondition<Boolean> gone(final BySelector selector) {
+ @NonNull
+ public static SearchCondition<Boolean> gone(@NonNull BySelector selector) {
return new SearchCondition<Boolean>() {
@Override
Boolean apply(Searchable container) {
@@ -45,7 +51,8 @@
* Returns a {@link SearchCondition} that is satisfied when at least one element matching the
* selector can be found.
*/
- public static SearchCondition<Boolean> hasObject(final BySelector selector) {
+ @NonNull
+ public static SearchCondition<Boolean> hasObject(@NonNull BySelector selector) {
return new SearchCondition<Boolean>() {
@Override
Boolean apply(Searchable container) {
@@ -58,7 +65,8 @@
* Returns a {@link SearchCondition} that is satisfied when at least one element matching the
* selector can be found. The condition will return the first matching element.
*/
- public static SearchCondition<UiObject2> findObject(final BySelector selector) {
+ @NonNull
+ public static SearchCondition<UiObject2> findObject(@NonNull BySelector selector) {
return new SearchCondition<UiObject2>() {
@Override
UiObject2 apply(Searchable container) {
@@ -71,7 +79,8 @@
* Returns a {@link SearchCondition} that is satisfied when at least one element matching the
* selector can be found. The condition will return all matching elements.
*/
- public static SearchCondition<List<UiObject2>> findObjects(final BySelector selector) {
+ @NonNull
+ public static SearchCondition<List<UiObject2>> findObjects(@NonNull BySelector selector) {
return new SearchCondition<List<UiObject2>>() {
@Override
List<UiObject2> apply(Searchable container) {
@@ -89,6 +98,7 @@
*
* @param isCheckable Whether the object should be checkable to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> checkable(final boolean isCheckable) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -103,6 +113,7 @@
*
* @param isChecked Whether the object should be checked to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> checked(final boolean isChecked) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -117,6 +128,7 @@
*
* @param isClickable Whether the object should be clickable to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> clickable(final boolean isClickable) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -131,6 +143,7 @@
*
* @param isEnabled Whether the object should be enabled to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> enabled(final boolean isEnabled) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -145,6 +158,7 @@
*
* @param isFocusable Whether the object should be focusable to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> focusable(final boolean isFocusable) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -159,6 +173,7 @@
*
* @param isFocused Whether the object should be focused to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> focused(final boolean isFocused) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -173,6 +188,7 @@
*
* @param isLongClickable Whether the object should be long clickable to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> longClickable(final boolean isLongClickable) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -187,6 +203,7 @@
*
* @param isScrollable Whether the object should be scrollable to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> scrollable(final boolean isScrollable) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -201,6 +218,7 @@
*
* @param isSelected Whether the object should be selected to satisfy this condition.
*/
+ @NonNull
public static UiObject2Condition<Boolean> selected(final boolean isSelected) {
return new UiObject2Condition<Boolean>() {
@Override
@@ -214,7 +232,8 @@
* Returns a condition that is satisfied when the object's content description matches the given
* regex.
*/
- public static UiObject2Condition<Boolean> descMatches(final Pattern regex) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descMatches(@NonNull Pattern regex) {
return new UiObject2Condition<Boolean>() {
@Override
Boolean apply(UiObject2 object) {
@@ -228,7 +247,8 @@
* Returns a condition that is satisfied when the object's content description matches the given
* regex.
*/
- public static UiObject2Condition<Boolean> descMatches(String regex) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descMatches(@NonNull String regex) {
return descMatches(Pattern.compile(regex));
}
@@ -236,7 +256,8 @@
* Returns a condition that is satisfied when the object's content description exactly matches
* the given string.
*/
- public static UiObject2Condition<Boolean> descEquals(String contentDescription) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descEquals(@NonNull String contentDescription) {
return descMatches(Pattern.compile(Pattern.quote(contentDescription)));
}
@@ -244,7 +265,8 @@
* Returns a condition that is satisfied when the object's content description contains the
* given string.
*/
- public static UiObject2Condition<Boolean> descContains(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descContains(@NonNull String substring) {
return descMatches(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
}
@@ -252,7 +274,8 @@
* Returns a condition that is satisfied when the object's content description starts with the
* given string.
*/
- public static UiObject2Condition<Boolean> descStartsWith(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descStartsWith(@NonNull String substring) {
return descMatches(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
}
@@ -260,14 +283,16 @@
* Returns a condition that is satisfied when the object's content description ends with the
* given string.
*/
- public static UiObject2Condition<Boolean> descEndsWith(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> descEndsWith(@NonNull String substring) {
return descMatches(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
}
/**
* Returns a condition that is satisfied when the object's text value matches the given regex.
*/
- public static UiObject2Condition<Boolean> textMatches(final Pattern regex) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textMatches(@NonNull Pattern regex) {
return new UiObject2Condition<Boolean>() {
@Override
Boolean apply(UiObject2 object) {
@@ -280,11 +305,17 @@
/**
* Returns a condition that is satisfied when the object's text value matches the given regex.
*/
- public static UiObject2Condition<Boolean> textMatches(String regex) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textMatches(@NonNull String regex) {
return textMatches(Pattern.compile(regex));
}
- public static UiObject2Condition<Boolean> textNotEquals(final String text) {
+ /**
+ * Returns a condition that is satisfied when the object's text value does not match the
+ * given string.
+ */
+ @NonNull
+ public static UiObject2Condition<Boolean> textNotEquals(@NonNull String text) {
return new UiObject2Condition<Boolean>() {
@Override
Boolean apply(UiObject2 object) {
@@ -297,14 +328,16 @@
* Returns a condition that is satisfied when the object's text value exactly matches the given
* string.
*/
- public static UiObject2Condition<Boolean> textEquals(String text) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textEquals(@NonNull String text) {
return textMatches(Pattern.compile(Pattern.quote(text)));
}
/**
* Returns a condition that is satisfied when the object's text value contains the given string.
*/
- public static UiObject2Condition<Boolean> textContains(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textContains(@NonNull String substring) {
return textMatches(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
}
@@ -312,7 +345,8 @@
* Returns a condition that is satisfied when the object's text value starts with the given
* string.
*/
- public static UiObject2Condition<Boolean> textStartsWith(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textStartsWith(@NonNull String substring) {
return textMatches(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
}
@@ -320,7 +354,8 @@
* Returns a condition that is satisfied when the object's text value ends with the given
* string.
*/
- public static UiObject2Condition<Boolean> textEndsWith(String substring) {
+ @NonNull
+ public static UiObject2Condition<Boolean> textEndsWith(@NonNull String substring) {
return textMatches(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
}
@@ -328,6 +363,7 @@
// Event conditions
/** Returns a condition that depends on a new window having appeared. */
+ @NonNull
public static EventCondition<Boolean> newWindow() {
return new EventCondition<Boolean>() {
private int mMask = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
@@ -352,7 +388,8 @@
*
* @param direction The direction of the scroll.
*/
- public static EventCondition<Boolean> scrollFinished(final Direction direction) {
+ @NonNull
+ public static EventCondition<Boolean> scrollFinished(@NonNull Direction direction) {
return new EventCondition<Boolean>() {
private Direction mDirection = direction;
private Boolean mResult = null;
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
new file mode 100644
index 0000000..5f68f6c
--- /dev/null
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+import android.webkit.CookieManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+public class CookieManagerCompatTest {
+ @Test
+ public void testGetCookieInfo() throws Throwable {
+ WebkitUtils.checkFeature(WebViewFeature.GET_COOKIE_INFO);
+ String url = "http://www.example.com";
+ String cookie = "foo=bar; domain=.example.com; path=/";
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.setCookie(url, cookie);
+
+ List<String> cookies = CookieManagerCompat.getCookieInfo(
+ cookieManager, url);
+ assertEquals(1, cookies.size());
+ assertEquals(cookie, cookies.get(0));
+ }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
new file mode 100644
index 0000000..d3ca97a
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import android.webkit.CookieManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+import androidx.webkit.internal.ApiFeature;
+import androidx.webkit.internal.CookieManagerAdapter;
+import androidx.webkit.internal.WebViewFeatureInternal;
+import androidx.webkit.internal.WebViewGlueCommunicator;
+
+import java.util.List;
+
+/**
+ * Compatibility version of {@link android.webkit.CookieManager}
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class CookieManagerCompat {
+ private CookieManagerCompat() {}
+
+ /**
+ * Gets all the cookies for the given URL along with their set attributes.
+ * The cookies are returned in the format of the HTTP 'Set-Cookie' header as defined in
+ * <a href="https://httpwg.org/specs/rfc6265.html#sane-set-cookie-syntax">the RFC6265 spec.</a>
+ * eg. "name=value; domain=.example.com; path=/"
+ *
+ * @param url the URL for which the API retrieves all available cookies.
+ * @return the cookies as a list of strings.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RequiresFeature(name = WebViewFeature.GET_COOKIE_INFO,
+ enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+ public static @NonNull List<String> getCookieInfo(@NonNull CookieManager cookieManager,
+ @NonNull String url) {
+ ApiFeature.NoFramework feature = WebViewFeatureInternal.GET_COOKIE_INFO;
+ if (feature.isSupportedByWebView()) {
+ return getAdapter(cookieManager).getCookieInfo(url);
+ } else {
+ throw WebViewFeatureInternal.getUnsupportedOperationException();
+ }
+ }
+
+ private static CookieManagerAdapter getAdapter(CookieManager cookieManager) {
+ return WebViewGlueCommunicator.getCompatConverter().convertCookieManager(cookieManager);
+ }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 9f13c4d..ef39f1d 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
+import android.webkit.CookieManager;
import android.webkit.ValueCallback;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
@@ -99,6 +100,7 @@
ALGORITHMIC_DARKENING,
REQUESTED_WITH_HEADER_CONTROL,
ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
+ GET_COOKIE_INFO,
})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -503,6 +505,15 @@
"ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
/**
+ * Feature for {@link #isFeatureSupported(String)}.
+ * This feature covers
+ * {@link CookieManagerCompat#getCookieInfo(CookieManager, String)}.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+
+ /**
* Return whether a feature is supported at run-time. On devices running Android version {@link
* android.os.Build.VERSION_CODES#LOLLIPOP} and higher, this will check whether a feature is
* supported, depending on the combination of the desired feature, the Android version of
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java
new file mode 100644
index 0000000..98fe199
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit.internal;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.support_lib_boundary.WebViewCookieManagerBoundaryInterface;
+
+import java.util.List;
+
+/**
+ * Adapter between CookieManagerCompat and
+ * {@link org.chromium.support_lib_boundary.CookieManagerBoundaryInterface} (the
+ * corresponding interface shared with the support library glue in the WebView APK).
+ */
+public class CookieManagerAdapter {
+ private final WebViewCookieManagerBoundaryInterface mBoundaryInterface;
+
+ public CookieManagerAdapter(@NonNull WebViewCookieManagerBoundaryInterface boundaryInterface) {
+ mBoundaryInterface = boundaryInterface;
+ }
+
+ /**
+ * Adapter method for {@link androidx.webkit.CookieManagerCompat#getCookieInfo}.
+ */
+ public @NonNull List<String> getCookieInfo(@NonNull String url) {
+ return mBoundaryInterface.getCookieInfo(url);
+ }
+}
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 c3fa35b..c2c16cd 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -473,7 +473,6 @@
new ApiFeature.NoFramework(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
Features.REQUESTED_WITH_HEADER_CONTROL);
-
/**
* This feature covers
* {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)} and
@@ -482,6 +481,13 @@
public static final ApiFeature.NoFramework ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
new ApiFeature.NoFramework(WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
Features.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY);
+
+ /**
+ * This feature covers
+ * {@link androidx.webkit.CookieManager#getCookieInfo(CookieManager, String)}.
+ */
+ public static final ApiFeature.NoFramework GET_COOKIE_INFO =
+ new ApiFeature.NoFramework(WebViewFeature.GET_COOKIE_INFO, Features.GET_COOKIE_INFO);
// --- Add new feature constants above this line ---
private WebViewFeatureInternal() {
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
index ef80a91..2d7327d4 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
@@ -16,6 +16,7 @@
package androidx.webkit.internal;
+import android.webkit.CookieManager;
import android.webkit.SafeBrowsingResponse;
import android.webkit.ServiceWorkerWebSettings;
import android.webkit.WebMessagePort;
@@ -31,6 +32,7 @@
import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface;
import org.chromium.support_lib_boundary.WebResourceRequestBoundaryInterface;
import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
+import org.chromium.support_lib_boundary.WebViewCookieManagerBoundaryInterface;
import org.chromium.support_lib_boundary.WebkitToCompatConverterBoundaryInterface;
import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
@@ -160,4 +162,16 @@
@NonNull /* SupportLibWebMessagePort */ InvocationHandler webMessagePort) {
return (WebMessagePort) mImpl.convertWebMessagePort(webMessagePort);
}
+
+ /**
+ * Return a CookieManagerAdapter linked to cookieManager such that calls on either of those
+ * objects affect the other object. That CookieManagerAdapter can be used to implement
+ * {@link androidx.webkit.CookieManagerCompat}.
+ */
+ @NonNull
+ public CookieManagerAdapter convertCookieManager(@NonNull CookieManager cookieManager) {
+ return new CookieManagerAdapter(BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+ WebViewCookieManagerBoundaryInterface.class,
+ mImpl.convertCookieManager(cookieManager)));
+ }
}