Merge "[Material3][List] Add experimental API tag to default and color objects." into androidx-main
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 81f3292..6c342ce 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -18,7 +18,7 @@
// Required to make activity 1.5.0-rc01 dependencies resolve.
implementation("androidx.core:core-ktx:1.8.0")
- implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.0")
+ implementation(libs.kotlinStdlib)
implementation(projectOrArtifact(":emoji2:emoji2"))
implementation(projectOrArtifact(":emoji2:emoji2-views-helper"))
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
index b8c582d..07064be 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/view/ContextThemeWrapper.java
@@ -118,7 +118,9 @@
private Resources getResourcesInternal() {
if (mResources == null) {
- if (isEmptyConfiguration(mOverrideConfiguration)) {
+ if (mOverrideConfiguration == null
+ || (Build.VERSION.SDK_INT >= 26
+ && isEmptyConfiguration(mOverrideConfiguration))) {
// If we're not applying any overrides, use the base context's resources. On API
// 26+, this will avoid pulling in resources that share a backing implementation
// with the application context.
@@ -215,6 +217,7 @@
* @return {@code true} if the specified configuration is {@code null} or is a no-op when
* used as a configuration overlay
*/
+ @RequiresApi(26)
private static boolean isEmptyConfiguration(Configuration overrideConfiguration) {
if (overrideConfiguration == null) {
return true;
diff --git a/bluetooth/bluetooth/build.gradle b/bluetooth/bluetooth/build.gradle
index 9bf971b..a83aefa 100644
--- a/bluetooth/bluetooth/build.gradle
+++ b/bluetooth/bluetooth/build.gradle
@@ -24,6 +24,7 @@
dependencies {
implementation(libs.kotlinStdlib)
+ implementation 'androidx.annotation:annotation:1.4.0'
}
androidx {
@@ -36,4 +37,7 @@
android {
namespace "androidx.bluetooth"
+ defaultConfig {
+ minSdkVersion 21
+ }
}
diff --git a/bluetooth/bluetooth/src/main/AndroidManifest.xml b/bluetooth/bluetooth/src/main/AndroidManifest.xml
index 6abff90..24c3903 100644
--- a/bluetooth/bluetooth/src/main/AndroidManifest.xml
+++ b/bluetooth/bluetooth/src/main/AndroidManifest.xml
@@ -13,4 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
new file mode 100644
index 0000000..2aaf3a5
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothGattDescriptor.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.bluetooth
+
+import android.os.Build
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.bluetooth.utils.Bundleable
+
+import java.util.UUID
+
+/**
+ * @hide
+ */
+class BluetoothGattDescriptor internal constructor(
+ descriptor: android.bluetooth.BluetoothGattDescriptor
+) : Bundleable {
+ private val impl: GattDescriptorImpl =
+ if (Build.VERSION.SDK_INT >= 24) {
+ GattDescriptorImplApi24(descriptor)
+ } else {
+ GattDescriptorImplApi21(descriptor)
+ }
+ internal val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor
+ get() = impl.fwkDescriptor
+ val permissions: Int
+ get() = impl.permissions
+ val uuid: UUID
+ get() = impl.uuid
+ val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+ get() = impl.characteristic
+
+ constructor(uuid: UUID, permissions: Int) : this(
+ android.bluetooth.BluetoothGattDescriptor(
+ uuid,
+ permissions
+ )
+ )
+
+ companion object {
+ const val PERMISSION_READ = android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ
+ const val PERMISSION_READ_ENCRYPTED =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED
+ const val PERMISSION_READ_ENCRYPTED_MITM =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED_MITM
+ const val PERMISSION_WRITE = android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE
+ const val PERMISSION_WRITE_ENCRYPTED =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED
+ const val PERMISSION_WRITE_ENCRYPTED_MITM =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED_MITM
+ const val PERMISSION_WRITE_SIGNED =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED
+ const val PERMISSION_WRITE_SIGNED_MITM =
+ android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED_MITM
+
+ val ENABLE_NOTIFICATION_VALUE = byteArrayOf(0x01, 0x00)
+ val ENABLE_INDICATION_VALUE = byteArrayOf(0x02, 0x00)
+ val DISABLE_NOTIFICATION_VALUE = byteArrayOf(0x00, 0x00)
+
+ internal fun keyForField(field: Int): String? {
+ return field.toString(Character.MAX_RADIX)
+ }
+
+ val CREATOR: Bundleable.Creator =
+ if (Build.VERSION.SDK_INT >= 24) GattDescriptorImplApi24.CREATOR
+ else GattDescriptorImplApi21.CREATOR
+ }
+
+ override fun toBundle(): Bundle {
+ return impl.toBundle()
+ }
+
+ private interface GattDescriptorImpl {
+ val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor
+ val permissions: Int
+ val uuid: UUID
+ val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+ fun toBundle(): Bundle
+ }
+
+ private open class GattDescriptorImplApi21(
+ descriptor: android.bluetooth.BluetoothGattDescriptor
+ ) : GattDescriptorImpl {
+ companion object {
+
+ internal const val FIELD_FWK_DESCRIPTOR_PERMISSIONS = 1
+ internal const val FIELD_FWK_DESCRIPTOR_UUID = 2
+ internal const val FIELD_FWK_DESCRIPTOR_INSTANCE = 3
+
+ val CREATOR: Bundleable.Creator =
+ object : Bundleable.Creator {
+
+ @Suppress("DEPRECATION")
+ override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
+ val permissions =
+ bundle.getInt(
+ keyForField(FIELD_FWK_DESCRIPTOR_PERMISSIONS),
+ -1
+ )
+ val uuid = bundle.getString(
+ keyForField(FIELD_FWK_DESCRIPTOR_UUID),
+ ) ?: throw IllegalArgumentException("Bundle doesn't include uuid")
+
+ if (permissions == -1) {
+ throw IllegalArgumentException("Bundle doesn't include permission")
+ }
+
+ val descriptor =
+ android.bluetooth.BluetoothGattDescriptor(
+ UUID.fromString(uuid),
+ permissions
+ )
+
+ descriptor.javaClass.getDeclaredField("mInstance").setInt(
+ descriptor, bundle.getInt(
+ keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), 0
+ )
+ )
+ return BluetoothGattDescriptor(descriptor)
+ }
+ }
+ }
+
+ override val fwkDescriptor: android.bluetooth.BluetoothGattDescriptor = descriptor
+ override val permissions: Int
+ get() = fwkDescriptor.permissions
+ override val uuid: UUID
+ get() = fwkDescriptor.uuid
+ override val characteristic: android.bluetooth.BluetoothGattCharacteristic?
+ get() = fwkDescriptor.characteristic
+
+ override fun toBundle(): Bundle {
+ val bundle = Bundle()
+ bundle.putString(keyForField(FIELD_FWK_DESCRIPTOR_UUID), uuid.toString())
+ bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_PERMISSIONS), permissions)
+ val instanceId: Int =
+ fwkDescriptor.javaClass.getDeclaredField("mInstance").getInt(fwkDescriptor)
+ bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), instanceId)
+ return bundle
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ private open class GattDescriptorImplApi24(
+ descriptor: android.bluetooth.BluetoothGattDescriptor
+ ) : GattDescriptorImplApi21(descriptor) {
+ companion object {
+ internal const val FIELD_FWK_DESCRIPTOR = 0
+ val CREATOR: Bundleable.Creator =
+ object : Bundleable.Creator {
+ @Suppress("DEPRECATION")
+ override fun fromBundle(bundle: Bundle): BluetoothGattDescriptor {
+ val fwkDescriptor =
+ bundle.getParcelable(
+ keyForField(FIELD_FWK_DESCRIPTOR)
+ ) ?: throw IllegalArgumentException("Bundle doesn't contain descriptor")
+ return BluetoothGattDescriptor(fwkDescriptor)
+ }
+ }
+ }
+
+ override fun toBundle(): Bundle {
+ val bundle = Bundle()
+ bundle.putParcelable(
+ keyForField(FIELD_FWK_DESCRIPTOR),
+ fwkDescriptor
+ )
+ return bundle
+ }
+ }
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt
new file mode 100644
index 0000000..73440dd
--- /dev/null
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/utils/Bundleable.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.bluetooth.utils
+
+import android.os.Bundle
+
+/**
+ * @hide
+ */
+interface Bundleable {
+ /** Returns a [Bundle] representing the information stored in this object. */
+ fun toBundle(): Bundle
+
+ /** Interface for the static `CREATOR` field of [Bundleable] classes. */
+ interface Creator {
+ /**
+ * Restores a [Bundleable] instance from a [Bundle] produced by [ ][Bundleable.toBundle].
+ *
+ *
+ * It guarantees the compatibility of [Bundle] representations produced by different
+ * versions of [Bundleable.toBundle] by providing best default values for missing
+ * fields. It throws an exception if any essential fields are missing.
+ */
+ fun fromBundle(bundle: Bundle): T
+ }
+}
\ No newline at end of file
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt b/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
index 94dd541..35c08d3 100644
--- a/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/AndroidXPluginTestContext.kt
@@ -165,16 +165,21 @@
fun AndroidXSelfTestProject.readPublishedFile(fileName: String) =
mavenLocalDir.resolve("$groupId/$artifactId/$version/$fileName").readText()
+ var printBuildFileOnFailure: Boolean = false
+
override fun toString(): String {
return buildMap {
put("root files", setup.rootDir.list().orEmpty().toList())
- setup.rootDir.listFiles().orEmpty().filter { it.isDirectory }.forEach { maybeGroupDir ->
- maybeGroupDir.listFiles().orEmpty().filter { it.isDirectory }.forEach {
- val maybeBuildFile = it.resolve("build.gradle")
- if (maybeBuildFile.exists()) {
- put(it.name + "/build.gradle", maybeBuildFile.readText())
+ if (printBuildFileOnFailure) {
+ setup.rootDir.listFiles().orEmpty().filter { it.isDirectory }
+ .forEach { maybeGroupDir ->
+ maybeGroupDir.listFiles().orEmpty().filter { it.isDirectory }.forEach {
+ val maybeBuildFile = it.resolve("build.gradle")
+ if (maybeBuildFile.exists()) {
+ put(it.name + "/build.gradle", maybeBuildFile.readText())
+ }
+ }
}
- }
}
}.toString()
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 2a58f28d..89a23c4 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -51,7 +51,6 @@
import com.android.build.gradle.TestExtension
import com.android.build.gradle.TestPlugin
import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.lint.AndroidLintTask
import com.android.build.gradle.internal.tasks.AnalyticsRecordingTask
import com.android.build.gradle.internal.tasks.ListingFileRedirectTask
import org.gradle.api.GradleException
@@ -383,17 +382,6 @@
}
}
- // Remove the lint and column attributes from generated lint baseline XML.
- project.tasks.withType(AndroidLintTask::class.java).configureEach { task ->
- if (task.name.startsWith("updateLintBaseline")) {
- task.doLast {
- task.outputs.files.find { it.name == "lint-baseline.xml" }?.let { file ->
- file.writeText(removeLineAndColumnAttributes(file.readText()))
- }
- }
- }
- }
-
// Remove the android:targetSdkVersion element from the manifest used for AARs.
project.extensions.getByType().onVariants { variant ->
project.tasks.register(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index bc5938e..fb481768 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -95,17 +95,19 @@
} else { null }
}
+ /**
+ * Configures all ios targets supported by AndroidX.
+ */
@JvmOverloads
- fun iosArm32(
+ fun ios(
block: Action? = null
- ): KotlinNativeTarget? {
- return if (project.enableMac()) {
- kotlinExtension.iosArm32().also {
- block?.execute(it)
- }
- } else { null }
+ ): List {
+ return listOfNotNull(
+ iosX64(block),
+ iosArm64(block),
+ iosSimulatorArm64(block)
+ )
}
-
@JvmOverloads
fun iosX64(
block: Action? = null
@@ -118,6 +120,17 @@
}
@JvmOverloads
+ fun iosSimulatorArm64(
+ block: Action? = null
+ ): KotlinNativeTarget? {
+ return if (project.enableMac()) {
+ kotlinExtension.iosSimulatorArm64().also {
+ block?.execute(it)
+ }
+ } else { null }
+ }
+
+ @JvmOverloads
fun linuxX64(
block: Action? = null
): KotlinNativeTargetWithHostTests? {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index fb9439f..fe5eb3d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -41,7 +41,6 @@
import org.gradle.api.tasks.bundling.ZipEntryCompression
import org.gradle.build.event.BuildEventsListenerRegistry
import org.gradle.kotlin.dsl.extra
-import org.jetbrains.kotlin.gradle.plugin.sources.CleanupStaleSourceSetMetadataEntriesService
abstract class AndroidXRootImplPlugin : Plugin {
@Suppress("UnstableApiUsage")
@@ -58,7 +57,6 @@
private fun Project.configureRootProject() {
project.validateAllAndroidxArgumentsAreRecognized()
tasks.register("listAndroidXProperties", ListAndroidXPropertiesTask::class.java)
- this.disableKmpKlibCleanupService()
setDependencyVersions()
configureKtlintCheckFile()
tasks.register(CheckExternalDependencyLicensesTask.TASK_NAME)
@@ -240,35 +238,4 @@
androidx.build.dependencies.agpVersion = getVersionByName("androidGradlePlugin")
androidx.build.dependencies.guavaVersion = getVersionByName("guavaJre")
}
-
- /**
- * This function applies the workaround for b/236850628.
- * There is a BuildService in KMP Gradle Plugin 1.7.0 that tries to cleanup unused klibs,
- * which is over-eager and causes missing dependencies in the Studio UI. This function simply
- * breaks the inputs of that cleanup function by intercepting the service creation and changing
- * its parameters.
- *
- * That code is already removed in KMP main branch and we should be able to remove this
- * workaround after updating to 1.7.20.
- *
- * see b/236850628#comment25 for more details.
- */
- private fun Project.disableKmpKlibCleanupService() {
- project.gradle.sharedServices.registrations.whenObjectAdded { serviceRegistration ->
- if (serviceRegistration.name == "cleanup-stale-sourceset-metadata") {
- val params = serviceRegistration.parameters as
- CleanupStaleSourceSetMetadataEntriesService.Parameters
- val tmpDirectory = project.layout.buildDirectory.dir("klib-cleanup-workaround")
- params.projectStorageRoot.set(
- tmpDirectory.map { it.asFile }
- )
- params.projectStorageDirectories.set(emptyMap())
- // make sure the store root is not changed afterwards. we don't need to set this
- // for projectStorageDirectories as the files are deleted from projectStorageRoot,
- // not projectStorageDirectories.
- // https://github.com/JetBrains/kotlin/blob/47beef16f38ea315bf4d7d4d3faf34b0b952b613/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt#L26
- params.projectStorageRoot.disallowChanges()
- }
- }
- }
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 2c76d84..288c32b8 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -19,6 +19,7 @@
import androidx.build.dependencyTracker.AffectedModuleDetector
import com.android.build.api.dsl.Lint
import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
+import com.android.build.gradle.internal.lint.AndroidLintTask
import java.io.File
import java.util.Locale
import org.gradle.api.GradleException
@@ -171,6 +172,17 @@
)
}
+ tasks.withType(AndroidLintTask::class.java).configureEach { task ->
+ // Remove the lint and column attributes from generated lint baseline XML.
+ if (task.name.startsWith("updateLintBaseline")) {
+ task.doLast {
+ task.outputs.files.find { it.name == "lint-baseline.xml" }?.let { file ->
+ file.writeText(removeLineAndColumnAttributes(file.readText()))
+ }
+ }
+ }
+ }
+
// Lint is configured entirely in finalizeDsl so that individual projects cannot easily
// disable individual checks in the DSL for any reason.
lint.apply {
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt b/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
index 5f2c434..0b79b6d 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/PublishExtension.kt
@@ -25,6 +25,7 @@
var js = Publish.UNSET
var mac = Publish.UNSET
var linux = Publish.UNSET
+ var ios = Publish.UNSET
/**
* List of platforms names which should be published to maven. e.g. ["jvm", "js"]
@@ -44,10 +45,13 @@
if (linux.shouldPublish()) {
platforms.addAll(linuxPlatforms)
}
+ if (ios.shouldPublish()) {
+ platforms.addAll(iosPlatforms)
+ }
return platforms
}
private val allExtendedPlatforms
- get() = listOf(jvm, js, mac, linux)
+ get() = listOf(jvm, js, mac, linux, ios)
private val allPlatforms
get() = listOf(android) + allExtendedPlatforms
private val activeExtendedPlatforms
@@ -73,7 +77,11 @@
private const val MAC_ARM_64 = "macosarm64"
private const val MAC_OSX_64 = "macosx64"
private const val LINUX_64 = "linuxx64"
+ private const val IOS_SIMULATOR_ARM_64 = "iossimulatorarm64"
+ private const val IOS_X_64 = "iosx64"
+ private const val IOS_ARM_64 = "iosarm64"
private val macPlatforms = listOf(MAC_ARM_64, MAC_OSX_64)
private val linuxPlatforms = listOf(LINUX_64)
+ private val iosPlatforms = listOf(IOS_SIMULATOR_ARM_64, IOS_ARM_64, IOS_X_64)
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
index a524d23..5f74052 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
@@ -45,11 +45,16 @@
* [android.hardware.camera2.CameraDevice] and [android.hardware.camera2.CameraCaptureSession] via
* the [CameraGraph] interface.
*/
-public class CameraPipe(config: Config, threadConfig: ThreadConfig = ThreadConfig()) {
+public class CameraPipe(config: Config) {
+
+ @Deprecated("threadConfig should be specified on config.")
+ @Suppress("UNUSED_PARAMETER")
+ public constructor(config: Config, threadConfig: ThreadConfig) : this(config)
+
private val debugId = cameraPipeIds.incrementAndGet()
private val component: CameraPipeComponent = DaggerCameraPipeComponent.builder()
.cameraPipeConfigModule(CameraPipeConfigModule(config))
- .threadConfigModule(ThreadConfigModule(threadConfig))
+ .threadConfigModule(ThreadConfigModule(config.threadConfig))
.build()
/**
diff --git a/car/app/app-automotive/api/current.txt b/car/app/app-automotive/api/current.txt
index fdfc2bc..e0b9e41 100644
--- a/car/app/app-automotive/api/current.txt
+++ b/car/app/app-automotive/api/current.txt
@@ -3,7 +3,7 @@
public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
ctor public BaseCarAppActivity();
- method public void bindToViewModel();
+ method public void bindToViewModel(androidx.car.app.SessionInfo);
method public android.content.ComponentName? retrieveServiceComponentName();
}
diff --git a/car/app/app-automotive/api/public_plus_experimental_current.txt b/car/app/app-automotive/api/public_plus_experimental_current.txt
index 83f0862..d7ebfbc 100644
--- a/car/app/app-automotive/api/public_plus_experimental_current.txt
+++ b/car/app/app-automotive/api/public_plus_experimental_current.txt
@@ -3,7 +3,7 @@
public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
ctor public BaseCarAppActivity();
- method public void bindToViewModel();
+ method public void bindToViewModel(androidx.car.app.SessionInfo);
method public android.content.ComponentName? retrieveServiceComponentName();
}
diff --git a/car/app/app-automotive/api/restricted_current.txt b/car/app/app-automotive/api/restricted_current.txt
index cf8af8b..9c3b7b0 100644
--- a/car/app/app-automotive/api/restricted_current.txt
+++ b/car/app/app-automotive/api/restricted_current.txt
@@ -3,7 +3,7 @@
public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity {
ctor public BaseCarAppActivity();
- method public void bindToViewModel();
+ method public void bindToViewModel(androidx.car.app.SessionInfo);
method public android.content.ComponentName? retrieveServiceComponentName();
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
index 9c0574f..13b2553 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/BaseCarAppActivity.java
@@ -40,6 +40,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.car.app.SessionInfo;
import androidx.car.app.activity.renderer.ICarAppActivity;
import androidx.car.app.activity.renderer.IInsetsListener;
import androidx.car.app.activity.renderer.IRendererCallback;
@@ -252,7 +253,7 @@
/**
* Binds the {@link BaseCarAppActivity} and it's view against the view model.
*/
- public void bindToViewModel() {
+ public void bindToViewModel(@NonNull SessionInfo sessionInfo) {
ComponentName serviceComponentName = retrieveServiceComponentName();
if (serviceComponentName == null) {
Log.e(LogTags.TAG, "Unspecified service class name");
@@ -261,7 +262,7 @@
}
CarAppViewModelFactory factory = CarAppViewModelFactory.getInstance(getApplication(),
- serviceComponentName);
+ serviceComponentName, sessionInfo);
mViewModel = new ViewModelProvider(this, factory).get(CarAppViewModel.class);
mViewModel.setActivity(this);
mViewModel.resetState();
@@ -329,9 +330,7 @@
}
private void onErrorChanged(@Nullable ErrorHandler.ErrorType errorType) {
- ThreadUtils.runOnMain(() -> {
- mErrorMessageView.setError(errorType);
- });
+ ThreadUtils.runOnMain(() -> mErrorMessageView.setError(errorType));
}
private void onStateChanged(@NonNull CarAppViewModel.State state) {
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
index faf5f42..c5d96a5 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppActivity.java
@@ -16,6 +16,10 @@
package androidx.car.app.activity;
+import static androidx.car.app.SessionInfo.DISPLAY_TYPE_MAIN;
+
+import static java.lang.System.identityHashCode;
+
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
@@ -23,6 +27,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.car.app.CarAppService;
+import androidx.car.app.SessionInfo;
/**
* The class representing a car app activity in the main display.
@@ -76,6 +81,15 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- bindToViewModel();
+
+ String identifier;
+ if (getIntent().getIdentifier() != null) {
+ identifier = getIntent().getIdentifier();
+ } else {
+ identifier = String.valueOf(identityHashCode(this));
+ }
+
+ bindToViewModel(new SessionInfo(DISPLAY_TYPE_MAIN,
+ identifier));
}
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
index 9b64f77..a1737df 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModel.java
@@ -30,6 +30,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
+import androidx.car.app.SessionInfo;
import androidx.car.app.activity.renderer.ICarAppActivity;
import androidx.car.app.activity.renderer.IInsetsListener;
import androidx.car.app.activity.renderer.IRendererCallback;
@@ -56,9 +57,12 @@
private final MutableLiveData mError = new MutableLiveData<>();
private final MutableLiveData mState = new MutableLiveData<>(State.IDLE);
private ServiceConnectionManager mServiceConnectionManager;
- @Nullable private IRendererCallback mIRendererCallback;
- @Nullable private IInsetsListener mIInsetsListener;
- @Nullable private Insets mInsets = Insets.NONE;
+ @Nullable
+ private IRendererCallback mIRendererCallback;
+ @Nullable
+ private IInsetsListener mIInsetsListener;
+ @Nullable
+ private Insets mInsets = Insets.NONE;
private static WeakReference sActivity = new WeakReference<>(null);
/** Possible view states */
@@ -73,14 +77,17 @@
ERROR,
}
- public CarAppViewModel(@NonNull Application application, @NonNull ComponentName componentName) {
+ public CarAppViewModel(@NonNull Application application,
+ @NonNull ComponentName componentName, @NonNull SessionInfo sessionInfo) {
super(application);
- mServiceConnectionManager = new ServiceConnectionManager(application, componentName, this);
+ mServiceConnectionManager = new ServiceConnectionManager(application, componentName,
+ sessionInfo, this);
}
@VisibleForTesting
- @NonNull ServiceConnectionManager getServiceConnectionManager() {
+ @NonNull
+ ServiceConnectionManager getServiceConnectionManager() {
return mServiceConnectionManager;
}
@@ -89,7 +96,8 @@
mServiceConnectionManager = serviceConnectionManager;
}
- @NonNull ServiceDispatcher getServiceDispatcher() {
+ @NonNull
+ ServiceDispatcher getServiceDispatcher() {
return mServiceConnectionManager.getServiceDispatcher();
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
index 3c9627d..4ce892c 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/CarAppViewModelFactory.java
@@ -23,6 +23,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.car.app.SessionInfo;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
@@ -40,11 +41,13 @@
Application mApplication;
ComponentName mComponentName;
+ SessionInfo mSessionInfo;
private CarAppViewModelFactory(@NonNull ComponentName componentName,
- @NonNull Application application) {
+ @NonNull Application application, @NonNull SessionInfo sessionInfo) {
mComponentName = componentName;
mApplication = application;
+ mSessionInfo = sessionInfo;
}
/**
@@ -54,10 +57,10 @@
*/
@NonNull
static CarAppViewModelFactory getInstance(Application application,
- ComponentName componentName) {
+ ComponentName componentName, SessionInfo sessionInfo) {
CarAppViewModelFactory instance = sInstances.get(componentName);
if (instance == null) {
- instance = new CarAppViewModelFactory(componentName, application);
+ instance = new CarAppViewModelFactory(componentName, application, sessionInfo);
sInstances.put(componentName, instance);
}
return instance;
@@ -67,6 +70,6 @@
@NonNull
@Override
public T create(@NonNull Class modelClass) {
- return (T) new CarAppViewModel(mApplication, mComponentName);
+ return (T) new CarAppViewModel(mApplication, mComponentName, mSessionInfo);
}
}
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
index 8d91a334..5d8ff94 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/ServiceConnectionManager.java
@@ -36,6 +36,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.car.app.HandshakeInfo;
+import androidx.car.app.SessionInfo;
import androidx.car.app.activity.renderer.ICarAppActivity;
import androidx.car.app.activity.renderer.IRendererService;
import androidx.car.app.versioning.CarAppApiLevels;
@@ -58,6 +59,7 @@
final ServiceConnectionListener mListener;
private final ComponentName mServiceComponentName;
+ private final SessionInfo mSessionInfo;
private final Context mContext;
private final ServiceDispatcher mServiceDispatcher;
private int mDisplayId;
@@ -79,8 +81,10 @@
public ServiceConnectionManager(@NonNull Context context,
@NonNull ComponentName serviceComponentName,
+ @NonNull SessionInfo sessionInfo,
@NonNull ServiceConnectionListener listener) {
mContext = context;
+ mSessionInfo = sessionInfo;
mListener = listener;
mServiceComponentName = serviceComponentName;
mServiceDispatcher = new ServiceDispatcher(listener, this::isBound);
@@ -197,6 +201,7 @@
}
Intent rendererIntent = new Intent(ACTION_RENDER);
+ SessionInfo.setBindData(rendererIntent, mSessionInfo);
List resolveInfoList =
mContext.getPackageManager()
.queryIntentServices(rendererIntent, PackageManager.GET_META_DATA);
@@ -284,7 +289,7 @@
private boolean updateIntent() {
ComponentName serviceComponentName = requireNonNull(mServiceComponentName);
Intent intent = requireNonNull(mIntent);
-
+ SessionInfo.setBindData(intent, mSessionInfo);
IRendererService service = mRendererService;
if (service == null) {
Log.e(LogTags.TAG, "Service dispatcher is not connected");
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
index dd0c09e..b7a7fc9 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppActivityTest.java
@@ -19,6 +19,8 @@
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_R;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
@@ -51,6 +53,7 @@
import android.view.inputmethod.InputConnection;
import androidx.car.app.CarAppService;
+import androidx.car.app.SessionInfo;
import androidx.car.app.activity.renderer.ICarAppActivity;
import androidx.car.app.activity.renderer.IInsetsListener;
import androidx.car.app.activity.renderer.IProxyInputConnection;
@@ -64,7 +67,6 @@
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ViewModelProvider;
import androidx.test.core.app.ActivityScenario;
-import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
@@ -79,19 +81,20 @@
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
public class CarAppActivityTest {
- private final ComponentName mRendererComponent = new ComponentName(
- ApplicationProvider.getApplicationContext(), getClass().getName());
+ public static final String INTENT_IDENTIFIER = "CarAppActivityTest";
+ private final ComponentName mRendererComponent = new ComponentName(getApplicationContext(),
+ getClass().getName());
private final String mFakeCarAppServiceClass = "com.fake.FakeCarAppService";
private final ComponentName mFakeCarAppServiceComponent = new ComponentName(
- ApplicationProvider.getApplicationContext(), mFakeCarAppServiceClass);
+ getApplicationContext(), mFakeCarAppServiceClass);
private final IRendererService mRenderService = mock(IRendererService.class);
- private final RenderServiceDelegate mRenderServiceDelegate =
- new RenderServiceDelegate(mRenderService);
+ private final RenderServiceDelegate mRenderServiceDelegate = new RenderServiceDelegate(
+ mRenderService);
@Before
public void setup() {
try {
- Application app = ApplicationProvider.getApplicationContext();
+ Application app = getApplicationContext();
// Register fake {@code CarAppService}
PackageManager packageManager = app.getPackageManager();
@@ -105,8 +108,7 @@
spm.addIntentFilterForService(mRendererComponent,
new IntentFilter(CarAppActivity.ACTION_RENDER));
- when(mRenderService.initialize(any(ICarAppActivity.class),
- any(ComponentName.class),
+ when(mRenderService.initialize(any(ICarAppActivity.class), any(ComponentName.class),
anyInt())).thenReturn(true);
when(mRenderService.onNewIntent(any(Intent.class), any(ComponentName.class),
anyInt())).thenReturn(true);
@@ -122,8 +124,7 @@
@Test
public void testRendererInitialization() {
runOnActivity((scenario, activity) -> {
- verify(mRenderService, times(1)).initialize(
- mRenderServiceDelegate.getCarAppActivity(),
+ verify(mRenderService, times(1)).initialize(mRenderServiceDelegate.getCarAppActivity(),
mFakeCarAppServiceComponent, activity.getDisplayId());
verify(mRenderService, times(1)).onNewIntent(activity.getIntent(),
mFakeCarAppServiceComponent, activity.getDisplayId());
@@ -162,12 +163,10 @@
verify(callback, times(1)).onResume();
// Add a test-specific lifecycle callback to activity.
- ActivityLifecycleCallbacks activityCallback = mock(
- ActivityLifecycleCallbacks.class);
+ ActivityLifecycleCallbacks activityCallback = mock(ActivityLifecycleCallbacks.class);
activity.registerActivityLifecycleCallbacks(activityCallback);
// Report service connection error.
- CarAppViewModel viewModel =
- new ViewModelProvider(activity).get(CarAppViewModel.class);
+ CarAppViewModel viewModel = new ViewModelProvider(activity).get(CarAppViewModel.class);
viewModel.onError(ErrorHandler.ErrorType.HOST_ERROR);
assertThat(activity.isFinishing()).isEqualTo(false);
@@ -197,19 +196,17 @@
runOnActivity((scenario, activity) -> {
ServiceConnectionManager serviceConnectionManager =
activity.mViewModel.getServiceConnectionManager();
- ServiceConnection serviceConnection =
- spy(serviceConnectionManager.getServiceConnection());
+ ServiceConnection serviceConnection = spy(
+ serviceConnectionManager.getServiceConnection());
serviceConnectionManager.setServiceConnection(serviceConnection);
// Destroy activity to force unbind.
scenario.moveToState(Lifecycle.State.DESTROYED);
// Verify Activity onDestroy even is reported to renderer.
- verify(mRenderService, times(1)).terminate(
- mFakeCarAppServiceComponent);
+ verify(mRenderService, times(1)).terminate(mFakeCarAppServiceComponent);
// Verify service connection is closed.
- verify(serviceConnection, times(1)).onServiceDisconnected(
- mRendererComponent);
+ verify(serviceConnection, times(1)).onServiceDisconnected(mRendererComponent);
assertThat(serviceConnectionManager.isBound()).isFalse();
});
@@ -223,8 +220,7 @@
IRendererCallback rendererCallback = mock(IRendererCallback.class);
ICarAppActivity carAppActivity = mRenderServiceDelegate.getCarAppActivity();
- carAppActivity.setSurfacePackage(
- Bundleable.create(new LegacySurfacePackage(callback)));
+ carAppActivity.setSurfacePackage(Bundleable.create(new LegacySurfacePackage(callback)));
carAppActivity.registerRendererCallback(rendererCallback);
// Verify back events on the activity are sent to host.
@@ -243,11 +239,9 @@
int x = 50;
int y = 50;
int metaState = 0;
- MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y,
- metaState);
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, metaState);
activity.mSurfaceView.dispatchTouchEvent(event);
- ArgumentCaptor argument = ArgumentCaptor.forClass(
- MotionEvent.class);
+ ArgumentCaptor argument = ArgumentCaptor.forClass(MotionEvent.class);
verify(callback, times(1)).onTouchEvent(argument.capture());
// Compare string representations as equals in MotionEvent checks for same
// object.
@@ -306,15 +300,13 @@
View activityContainer = activity.mActivityContainerView;
View localContentContainer = activity.mLocalContentContainerView;
Insets systemWindowInsets = Insets.of(10, 20, 30, 40);
- WindowInsets windowInsets = new WindowInsetsCompat.Builder()
- .setInsets(WindowInsetsCompat.Type.systemBars(), systemWindowInsets)
- .build()
- .toWindowInsets();
+ WindowInsets windowInsets = new WindowInsetsCompat.Builder().setInsets(
+ WindowInsetsCompat.Type.systemBars(),
+ systemWindowInsets).build().toWindowInsets();
activityContainer.onApplyWindowInsets(windowInsets);
// Verify that the host is notified and insets are not handled locally
- verify(insetsListener).onInsetsChanged(
- eq(systemWindowInsets.toPlatformInsets()));
+ verify(insetsListener).onInsetsChanged(eq(systemWindowInsets.toPlatformInsets()));
assertThat(activityContainer.getPaddingBottom()).isEqualTo(0);
assertThat(activityContainer.getPaddingTop()).isEqualTo(0);
assertThat(activityContainer.getPaddingLeft()).isEqualTo(0);
@@ -346,6 +338,62 @@
});
}
+ @Test
+ public void testLaunchWithIdentifier_passesAlongValues() {
+ ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+ newIntent.setIdentifier(INTENT_IDENTIFIER);
+ try (ActivityScenario scenario = ActivityScenario.launch(newIntent)) {
+ scenario.onActivity(activity -> {
+ try {
+ verify(mRenderService, times(1)).initialize(
+ mRenderServiceDelegate.getCarAppActivity(), mFakeCarAppServiceComponent,
+ activity.getDisplayId());
+ verify(mRenderService, times(1)).onNewIntent(intentArgumentCaptor.capture(),
+ eq(mFakeCarAppServiceComponent), eq(activity.getDisplayId()));
+
+ Intent intent = intentArgumentCaptor.getValue();
+ SessionInfo si = new SessionInfo(intent);
+
+ assertThat(si.getDisplayType()).isEqualTo(SessionInfo.DISPLAY_TYPE_MAIN);
+ assertThat(intent.getIdentifier()).isEqualTo(si.toString());
+ } catch (Exception e) {
+ fail(Log.getStackTraceString(e));
+ }
+ });
+
+ }
+
+ }
+
+ @Test
+ public void testLaunchWithoutIdentifier_setsRandomIdValue() {
+ ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ Intent newIntent = new Intent(getApplicationContext(), CarAppActivity.class);
+ try (ActivityScenario scenario = ActivityScenario.launch(newIntent)) {
+ scenario.onActivity(activity -> {
+ try {
+ verify(mRenderService, times(1)).initialize(
+ mRenderServiceDelegate.getCarAppActivity(), mFakeCarAppServiceComponent,
+ activity.getDisplayId());
+ verify(mRenderService, times(1)).onNewIntent(intentArgumentCaptor.capture(),
+ eq(mFakeCarAppServiceComponent), eq(activity.getDisplayId()));
+
+ Intent intent = intentArgumentCaptor.getValue();
+ SessionInfo si = new SessionInfo(intent);
+
+ assertThat(si.getDisplayType()).isEqualTo(SessionInfo.DISPLAY_TYPE_MAIN);
+ assertThat(intent.getIdentifier()).isNotEqualTo(INTENT_IDENTIFIER);
+ assertThat(intent.getIdentifier()).isEqualTo(si.toString());
+ } catch (Exception e) {
+ fail(Log.getStackTraceString(e));
+ }
+ });
+
+ }
+
+ }
+
interface CarActivityAction {
void accept(ActivityScenario scenario, CarAppActivity activity)
throws Exception;
@@ -361,6 +409,7 @@
fail(Log.getStackTraceString(e));
}
});
+
}
}
}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
index 7650a2d..8625a22 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelFactoryTest.java
@@ -16,6 +16,8 @@
package androidx.car.app.activity;
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
@@ -42,10 +44,10 @@
@Test
public void getInstance_sameKey_returnsSame() {
CarAppViewModelFactory factory1 = CarAppViewModelFactory.getInstance(mApplication,
- TEST_COMPONENT_NAME_1);
+ TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
CarAppViewModelFactory factory2 = CarAppViewModelFactory.getInstance(mApplication,
- TEST_COMPONENT_NAME_1);
+ TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
assertThat(factory1).isEqualTo(factory2);
}
@@ -53,10 +55,10 @@
@Test
public void getInstance_differentKeys_returnsDifferent() {
CarAppViewModelFactory factory1 = CarAppViewModelFactory.getInstance(mApplication,
- TEST_COMPONENT_NAME_1);
+ TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
CarAppViewModelFactory factory2 = CarAppViewModelFactory.getInstance(mApplication,
- TEST_COMPONENT_NAME_2);
+ TEST_COMPONENT_NAME_2, DEFAULT_SESSION_INFO);
assertThat(factory1).isNotEqualTo(factory2);
}
@@ -64,7 +66,7 @@
@Test
public void create_correctComponentName() {
CarAppViewModelFactory factory = CarAppViewModelFactory.getInstance(mApplication,
- TEST_COMPONENT_NAME_1);
+ TEST_COMPONENT_NAME_1, DEFAULT_SESSION_INFO);
CarAppViewModel viewModel = factory.create(CarAppViewModel.class);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
index 3c7741f3..353977c 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/CarAppViewModelTest.java
@@ -16,6 +16,8 @@
package androidx.car.app.activity;
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -39,7 +41,7 @@
/** Tests for {@link CarAppViewModel} */
@RunWith(RobolectricTestRunner.class)
-@Config(instrumentedPackages = { "androidx.car.app.activity" })
+@Config(instrumentedPackages = {"androidx.car.app.activity"})
@DoNotInstrument
public class CarAppViewModelTest {
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
@@ -49,7 +51,7 @@
private final Application mApplication = ApplicationProvider.getApplicationContext();
private CarAppViewModel mCarAppViewModel;
- private CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
+ private final CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
private ICarAppActivity mICarAppActivity;
private ShadowLooper mMainLooper;
private final ServiceConnectionManager mServiceConnectionManager =
@@ -57,7 +59,8 @@
@Before
public void setUp() {
- mCarAppViewModel = new CarAppViewModel(mApplication, TEST_COMPONENT_NAME);
+ mCarAppViewModel = new CarAppViewModel(mApplication, TEST_COMPONENT_NAME,
+ DEFAULT_SESSION_INFO);
mCarAppViewModel.setActivity(mCarAppActivity);
mICarAppActivity = mock(ICarAppActivity.class);
mMainLooper = shadowOf(mApplication.getMainLooper());
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
index 6f987f8..ca31699 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ResultManagerAutomotiveTest.java
@@ -16,6 +16,8 @@
package androidx.car.app.activity;
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -43,19 +45,20 @@
/** Tests for {@link ResultManagerAutomotive}. */
@RunWith(RobolectricTestRunner.class)
-@Config(instrumentedPackages = { "androidx.car.app.activity" })
+@Config(instrumentedPackages = {"androidx.car.app.activity"})
@DoNotInstrument
public class ResultManagerAutomotiveTest {
private final ComponentName mRendererComponent = new ComponentName(
ApplicationProvider.getApplicationContext(), getClass().getName());
private final Application mApplication = ApplicationProvider.getApplicationContext();
- private CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
- private ResultManagerAutomotive mResultManager = new ResultManagerAutomotive();
+ private final CarAppActivity mCarAppActivity = mock(CarAppActivity.class);
+ private final ResultManagerAutomotive mResultManager = new ResultManagerAutomotive();
private CarAppViewModel mCarAppViewModel;
@Before
public void setUp() {
- mCarAppViewModel = new CarAppViewModel(mApplication, mRendererComponent);
+ mCarAppViewModel = new CarAppViewModel(mApplication, mRendererComponent,
+ DEFAULT_SESSION_INFO);
}
@Test
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
index 5bfc022..72c040b 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/activity/ServiceConnectionManagerTest.java
@@ -18,11 +18,14 @@
import static android.os.Looper.getMainLooper;
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -38,6 +41,7 @@
import android.util.Log;
import androidx.car.app.HandshakeInfo;
+import androidx.car.app.SessionInfo;
import androidx.car.app.activity.renderer.ICarAppActivity;
import androidx.car.app.activity.renderer.IRendererService;
import androidx.car.app.serialization.Bundleable;
@@ -47,6 +51,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
import org.robolectric.shadows.ShadowApplication;
@@ -72,7 +77,7 @@
new RenderServiceDelegate(mRenderService);
private final CarAppViewModel mViewModel =
new CarAppViewModel(ApplicationProvider.getApplicationContext(),
- mFakeCarAppServiceComponent);
+ mFakeCarAppServiceComponent, DEFAULT_SESSION_INFO);
private final ServiceConnectionManager mServiceConnectionManager =
mViewModel.getServiceConnectionManager();
private final ShadowLooper mMainLooper = shadowOf(getMainLooper());
@@ -125,7 +130,7 @@
public void testBind_unbound_bindsToRenderer() {
setupCarAppActivityForTesting();
ICarAppActivity iCarAppActivity = mock(ICarAppActivity.class);
-
+ ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
mServiceConnectionManager.bind(TEST_INTENT, iCarAppActivity, TEST_DISPLAY_ID);
mMainLooper.idle();
try {
@@ -133,12 +138,17 @@
assertThat(mViewModel.getError().getValue()).isNull();
verify(mRenderService).initialize(iCarAppActivity, mFakeCarAppServiceComponent,
TEST_DISPLAY_ID);
- verify(mRenderService).onNewIntent(TEST_INTENT, mFakeCarAppServiceComponent,
- TEST_DISPLAY_ID);
+ verify(mRenderService).onNewIntent(intentArgumentCaptor.capture(),
+ eq(mFakeCarAppServiceComponent),
+ eq(TEST_DISPLAY_ID));
} catch (RemoteException e) {
fail(Log.getStackTraceString(e));
}
assertThat(mServiceConnectionManager.isBound()).isTrue();
+ assertThat(intentArgumentCaptor.getValue()).isEqualTo(TEST_INTENT);
+ assertThat(intentArgumentCaptor.getValue().getExtras()).isNotNull();
+ assertThat(new SessionInfo(intentArgumentCaptor.getValue())).isEqualTo(
+ DEFAULT_SESSION_INFO);
}
@Test
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 347b66d..a696657a 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -159,11 +159,14 @@
@androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
ctor public SessionInfo(int, String);
+ ctor public SessionInfo(android.content.Intent);
method public int getDisplayType();
method public String getSessionId();
+ method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+ field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
}
public interface SurfaceCallback {
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 0556f7b..d69fc33 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -160,12 +160,15 @@
@androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
ctor public SessionInfo(int, String);
+ ctor public SessionInfo(android.content.Intent);
method public int getDisplayType();
method public String getSessionId();
method @androidx.car.app.annotations.ExperimentalCarApi public java.util.Set!>? getSupportedTemplates(int);
+ method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+ field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
}
public interface SurfaceCallback {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 347b66d..a696657a 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -159,11 +159,14 @@
@androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
ctor public SessionInfo(int, String);
+ ctor public SessionInfo(android.content.Intent);
method public int getDisplayType();
method public String getSessionId();
+ method public static void setBindData(android.content.Intent, androidx.car.app.SessionInfo);
field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+ field public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
}
public interface SurfaceCallback {
diff --git a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
index 3176923..8006c178 100644
--- a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
@@ -17,15 +17,24 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.NavigationTemplate;
+import androidx.car.app.serialization.Bundleable;
+import androidx.car.app.serialization.BundlerException;
import androidx.car.app.versioning.CarAppApiLevel;
import androidx.car.app.versioning.CarAppApiLevels;
@@ -41,6 +50,9 @@
public class SessionInfo {
private static final char DIVIDER = '/';
+ /** The key for a {@link Bundleable} extra containing the {@link SessionInfo} for a bind. */
+ public static final String EXTRA_SESSION_INFO = "androidx.car.app.extra.SESSION_INFO";
+
/** The primary infotainment display usually in the center column of the vehicle. */
public static final int DISPLAY_TYPE_MAIN = 0;
@@ -101,6 +113,53 @@
mSessionId = sessionId;
}
+ /**
+ * Creates a new {@link SessionInfo} for a given bind {@code intent}
+ */
+ @SuppressWarnings("deprecation")
+ public SessionInfo(@NonNull Intent intent) {
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ throw new IllegalArgumentException(
+ "Expected the SessionInfo to be encoded in the bind intent extras, but the "
+ + "extras were null.");
+ }
+
+ Bundleable sessionInfoBundleable = extras.getParcelable(EXTRA_SESSION_INFO);
+ if (sessionInfoBundleable == null) {
+ throw new IllegalArgumentException(
+ "Expected the SessionInfo to be encoded in the bind intent extras, but they "
+ + "couldn't be found in the extras.");
+ }
+
+ try {
+ SessionInfo info = (SessionInfo) sessionInfoBundleable.get();
+ this.mSessionId = info.mSessionId;
+ this.mDisplayType = info.mDisplayType;
+ } catch (BundlerException e) {
+ throw new IllegalArgumentException(
+ "Expected the SessionInfo to be encoded in the bind intent extras, but they "
+ + "were encoded improperly", e);
+ }
+ }
+
+ /**
+ * Adds the {@link SessionInfo} and associated identifier on the passed {@code intent} to
+ * pass to this service on bind.
+ */
+ public static void setBindData(@NonNull Intent intent, @NonNull SessionInfo sessionInfo) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Api29.setIdentifier(intent, sessionInfo.toString());
+ } else {
+ intent.setData(new Uri.Builder().path(sessionInfo.toString()).build());
+ }
+ try {
+ intent.putExtra(EXTRA_SESSION_INFO, Bundleable.create(sessionInfo));
+ } catch (BundlerException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// Required for Bundler
private SessionInfo() {
mSessionId = "main";
@@ -153,4 +212,25 @@
return this.getSessionId().equals(object.getSessionId())
&& this.getDisplayType() == object.getDisplayType();
}
+
+ /** Android Q method calls wrapped in a {@link RequiresApi} class to appease the compiler. */
+ @RequiresApi(Build.VERSION_CODES.Q)
+ private static class Api29 {
+ // Not instantiable
+ private Api29() {
+ }
+
+ /** Wrapper for {@link Intent#getIdentifier()}. */
+ @DoNotInline
+ @Nullable
+ static String getIdentifier(@NonNull Intent intent) {
+ return intent.getIdentifier();
+ }
+
+ /** Wrapper for {@link Intent#setIdentifier(String)}. */
+ @DoNotInline
+ static void setIdentifier(@NonNull Intent intent, @NonNull String identifier) {
+ intent.setIdentifier(identifier);
+ }
+ }
}
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
index 63c5c9c..f21bd8d 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
@@ -51,6 +51,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -196,6 +197,7 @@
}
@SuppressLint("NewApi")
+ @Ignore // b/238635208
@Test
public void onAppCreate_updatesTheConfiguration() {
Configuration configuration = new Configuration();
@@ -250,6 +252,7 @@
}
@SuppressLint("NewApi")
+ @Ignore // b/238635208
@Test
public void onConfigurationChanged_updatesTheConfiguration() throws RemoteException {
mCarAppBinder.onAppCreate(mMockCarHost, null, new Configuration(),
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index 3ea8abb..bb49c17 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -55,6 +55,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -260,6 +261,7 @@
assertThat(name).isNull();
}
+ @Ignore // b/238635208
@Test
public void onConfigurationChanged_updatesTheConfiguration() {
Configuration configuration = new Configuration();
diff --git a/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java b/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
index 3712ac8..1c3a502 100644
--- a/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/SessionInfoTest.java
@@ -16,17 +16,24 @@
package androidx.car.app;
+import static androidx.car.app.SessionInfo.DEFAULT_SESSION_INFO;
import static androidx.car.app.SessionInfo.DISPLAY_TYPE_CLUSTER;
import static androidx.car.app.SessionInfo.DISPLAY_TYPE_MAIN;
+import static androidx.car.app.SessionInfo.EXTRA_SESSION_INFO;
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Build;
+
import androidx.car.app.model.Template;
import androidx.car.app.versioning.CarAppApiLevels;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.robolectric.annotation.internal.DoNotInstrument;
import java.util.Set;
@@ -122,4 +129,50 @@
anotherDisplayTypeMainSessionId.toString());
}
+ @Test
+ public void bind_insertsExtra() {
+ Intent intent = new Intent();
+ SessionInfo.setBindData(intent, DEFAULT_SESSION_INFO);
+
+ assertThat(intent.hasExtra(EXTRA_SESSION_INFO));
+ }
+
+ @Test
+ public void bind_from_returnsExpectedValue() {
+ Intent intent = new Intent();
+ SessionInfo.setBindData(intent, DEFAULT_SESSION_INFO);
+
+ SessionInfo info = new SessionInfo(intent);
+
+ assertThat(info).isEqualTo(DEFAULT_SESSION_INFO);
+
+ }
+
+ @SuppressLint("NewApi")
+ @Test
+ @Config(sdk = Build.VERSION_CODES.Q)
+ public void setBindData() {
+ SessionInfo testSessionInfo =
+ new SessionInfo(DISPLAY_TYPE_CLUSTER, "a-unique-session-id");
+ Intent intent = new Intent();
+ SessionInfo.setBindData(intent, testSessionInfo);
+
+ SessionInfo resultSessionInfo = new SessionInfo(intent);
+ assertThat(resultSessionInfo).isEqualTo(testSessionInfo);
+ assertThat(intent.getIdentifier()).isEqualTo(testSessionInfo.toString());
+ }
+
+ @Test
+ @Config(sdk = Build.VERSION_CODES.P)
+ public void setBindData_belowQ() {
+ SessionInfo testSessionInfo =
+ new SessionInfo(DISPLAY_TYPE_CLUSTER, "a-unique-session-id");
+ Intent intent = new Intent();
+ SessionInfo.setBindData(intent, testSessionInfo);
+
+ SessionInfo resultSessionInfo = new SessionInfo(intent);
+ assertThat(resultSessionInfo).isEqualTo(testSessionInfo);
+ assertThat(intent.getDataString()).isEqualTo(testSessionInfo.toString());
+ }
+
}
diff --git a/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java b/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
index 7e55652..46ae9a4 100644
--- a/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/notification/CarNotificationManagerTest.java
@@ -39,6 +39,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -94,6 +95,7 @@
mDefaultPendingIntent);
}
+ @Ignore // b/238635208
@SuppressWarnings("deprecation")
@Test
public void updateForCar_embedded_overridesAsExpected() {
@@ -122,6 +124,7 @@
assertThat(action.title).isEqualTo(EXTENDED_ACTION_TITLE);
}
+ @Ignore // b/238635208
@SuppressWarnings("deprecation")
@Test
public void updateForCar_notEmbedded_returnsTheSame() {
@@ -147,6 +150,7 @@
assertThat(action.title).isEqualTo(DEFAULT_ACTION_TITLE);
}
+ @Ignore // b/238635208
@SuppressWarnings("deprecation")
@Test
public void updateForCar_notExtended_marksAsExtended()
diff --git a/collection/collection/build.gradle b/collection/collection/build.gradle
index af379cf..784367d 100644
--- a/collection/collection/build.gradle
+++ b/collection/collection/build.gradle
@@ -36,6 +36,7 @@
macosX64()
macosArm64()
linuxX64()
+ ios()
sourceSets {
commonMain {
@@ -71,7 +72,8 @@
targets.all { target ->
if (target.platformType == KotlinPlatformType.native) {
target.compilations["main"].defaultSourceSet {
- if (target.konanTarget.family == Family.OSX) {
+ if (target.konanTarget.family == Family.OSX ||
+ target.konanTarget.family == Family.IOS) {
dependsOn(darwinMain)
} else if (target.konanTarget.family == Family.LINUX) {
dependsOn(linuxMain)
@@ -127,6 +129,7 @@
jvm = Publish.SNAPSHOT_AND_RELEASE
linux = Publish.SNAPSHOT_AND_RELEASE
mac = Publish.SNAPSHOT_AND_RELEASE
+ ios = Publish.SNAPSHOT_AND_RELEASE
}
mavenGroup = LibraryGroups.COLLECTION
inceptionYear = "2018"
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 44c0a94..ed94995 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -204,7 +204,7 @@
project: Project,
configuration: CompilerConfiguration
) {
- val KOTLIN_VERSION_EXPECTATION = "1.7.0"
+ val KOTLIN_VERSION_EXPECTATION = "1.7.10"
KotlinCompilerVersion.getVersion()?.let { version ->
val suppressKotlinVersionCheck = configuration.get(
ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index b66d0de..7b54efd 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -631,7 +631,7 @@
// soon be _more_ visible, so don't scroll.
if (!containerShrunk) return
- val focusedChild = focusedChild ?: return
+ val focusedChild = focusedChild?.takeIf { it.isAttached } ?: return
val focusedBounds = coordinates.localBoundingBoxOf(focusedChild, clipBounds = false)
// In order to check if we need to scroll to bring the focused child into view, it's not
diff --git a/compose/ui/ui-geometry/api/current.txt b/compose/ui/ui-geometry/api/current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/current.txt
+++ b/compose/ui/ui-geometry/api/current.txt
@@ -32,7 +32,7 @@
public final class MutableRect {
ctor public MutableRect(float left, float top, float right, float bottom);
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public float getBottom();
method public inline float getHeight();
method public float getLeft();
@@ -105,7 +105,7 @@
method public float component2();
method public float component3();
method public float component4();
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
method public float getBottom();
@@ -180,7 +180,7 @@
method public long component6-kKHJgLs();
method public long component7-kKHJgLs();
method public long component8-kKHJgLs();
- method public boolean contains(long point);
+ method public operator boolean contains(long point);
method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
method public float getBottom();
method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-geometry/api/public_plus_experimental_current.txt
@@ -32,7 +32,7 @@
public final class MutableRect {
ctor public MutableRect(float left, float top, float right, float bottom);
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public float getBottom();
method public inline float getHeight();
method public float getLeft();
@@ -105,7 +105,7 @@
method public float component2();
method public float component3();
method public float component4();
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
method public float getBottom();
@@ -180,7 +180,7 @@
method public long component6-kKHJgLs();
method public long component7-kKHJgLs();
method public long component8-kKHJgLs();
- method public boolean contains(long point);
+ method public operator boolean contains(long point);
method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
method public float getBottom();
method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/api/restricted_current.txt b/compose/ui/ui-geometry/api/restricted_current.txt
index 936778a..583e373 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -32,7 +32,7 @@
public final class MutableRect {
ctor public MutableRect(float left, float top, float right, float bottom);
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public float getBottom();
method public inline float getHeight();
method public float getLeft();
@@ -105,7 +105,7 @@
method public float component2();
method public float component3();
method public float component4();
- method public boolean contains(long offset);
+ method public operator boolean contains(long offset);
method public androidx.compose.ui.geometry.Rect copy(float left, float top, float right, float bottom);
method @androidx.compose.runtime.Stable public androidx.compose.ui.geometry.Rect deflate(float delta);
method public float getBottom();
@@ -180,7 +180,7 @@
method public long component6-kKHJgLs();
method public long component7-kKHJgLs();
method public long component8-kKHJgLs();
- method public boolean contains(long point);
+ method public operator boolean contains(long point);
method public androidx.compose.ui.geometry.RoundRect copy-MDFrsts(float left, float top, float right, float bottom, long topLeftCornerRadius, long topRightCornerRadius, long bottomRightCornerRadius, long bottomLeftCornerRadius);
method public float getBottom();
method public long getBottomLeftCornerRadius();
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
index b00c587..1e2d5b1 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/MutableRect.kt
@@ -77,7 +77,7 @@
* Rectangles include their top and left edges but exclude their bottom and
* right edges.
*/
- fun contains(offset: Offset): Boolean {
+ operator fun contains(offset: Offset): Boolean {
return offset.x >= left && offset.x < right && offset.y >= top && offset.y < bottom
}
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
index 6c976ed..5230b42 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Rect.kt
@@ -244,7 +244,7 @@
* Rectangles include their top and left edges but exclude their bottom and
* right edges.
*/
- fun contains(offset: Offset): Boolean {
+ operator fun contains(offset: Offset): Boolean {
return offset.x >= left && offset.x < right && offset.y >= top && offset.y < bottom
}
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
index 9e1484c..2ce06a3 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/RoundRect.kt
@@ -122,7 +122,7 @@
* using this method, prefer to reuse existing [RoundRect]s rather than
* recreating the object each time.
*/
- fun contains(point: Offset): Boolean {
+ operator fun contains(point: Offset): Boolean {
if (point.x < left || point.x >= right || point.y < top || point.y >= bottom) {
return false; // outside bounding box
}
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
index 1750492..ce9a9c9 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/MutableRectTest.kt
@@ -51,18 +51,18 @@
@Test
fun contains() {
val r = MutableRect(1f, 3f, 5f, 9f)
- assertTrue(r.contains(Offset(1f, 3f)))
- assertTrue(r.contains(Offset(3f, 3f)))
- assertFalse(r.contains(Offset(5f, 3f)))
- assertTrue(r.contains(Offset(1f, 6f)))
- assertTrue(r.contains(Offset(3f, 6f)))
- assertFalse(r.contains(Offset(5f, 6f)))
- assertFalse(r.contains(Offset(1f, 9f)))
- assertFalse(r.contains(Offset(3f, 9f)))
- assertFalse(r.contains(Offset(5f, 9f)))
- assertFalse(r.contains(Offset(0f, 0f)))
- assertFalse(r.contains(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)))
- assertFalse(r.contains(Offset(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)))
+ assertTrue(Offset(1f, 3f) in r)
+ assertTrue(Offset(3f, 3f) in r)
+ assertFalse(Offset(5f, 3f) in r)
+ assertTrue(Offset(1f, 6f) in r)
+ assertTrue(Offset(3f, 6f) in r)
+ assertFalse(Offset(5f, 6f) in r)
+ assertFalse(Offset(1f, 9f) in r)
+ assertFalse(Offset(3f, 9f) in r)
+ assertFalse(Offset(5f, 9f) in r)
+ assertFalse(Offset(0f, 0f) in r)
+ assertFalse(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) in r)
+ assertFalse(Offset(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY) in r)
}
@Test
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
index b2e5432..927b408 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RectTest.kt
@@ -223,17 +223,17 @@
fun `rect contains`() {
val rect = Rect(100f, 10f, 200f, 300f)
val offset = Offset(177f, 288f)
- assertTrue(rect.contains(offset))
+ assertTrue(offset in rect)
}
@Test
fun `rect does not contain`() {
val rect = Rect(100f, 10f, 200f, 300f)
val offset1 = Offset(201f, 150f)
- assertFalse(rect.contains(offset1))
+ assertFalse(offset1 in rect)
val offset2 = Offset(200f, 301f)
- assertFalse(rect.contains(offset2))
+ assertFalse(offset2 in rect)
}
@Test
diff --git a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
index 0880e8e..1f6aeac 100644
--- a/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
+++ b/compose/ui/ui-geometry/src/test/kotlin/androidx/compose/ui/geometry/RoundRectTest.kt
@@ -36,14 +36,14 @@
bottomLeft = CornerRadius.Zero
)
- assertFalse(roundRect.contains(Offset(1.0f, 1.0f)))
- assertFalse(roundRect.contains(Offset(1.1f, 1.1f)))
- assertTrue(roundRect.contains(Offset(1.15f, 1.15f)))
- assertFalse(roundRect.contains(Offset(2.0f, 1.0f)))
- assertFalse(roundRect.contains(Offset(1.93f, 1.07f)))
- assertFalse(roundRect.contains(Offset(1.97f, 1.7f)))
- assertTrue(roundRect.contains(Offset(1.7f, 1.97f)))
- assertTrue(roundRect.contains(Offset(1.0f, 1.99f)))
+ assertFalse(Offset(1.0f, 1.0f) in roundRect)
+ assertFalse(Offset(1.1f, 1.1f) in roundRect)
+ assertTrue(Offset(1.15f, 1.15f) in roundRect)
+ assertFalse(Offset(2.0f, 1.0f) in roundRect)
+ assertFalse(Offset(1.93f, 1.07f) in roundRect)
+ assertFalse(Offset(1.97f, 1.7f) in roundRect)
+ assertTrue(Offset(1.7f, 1.97f) in roundRect)
+ assertTrue(Offset(1.0f, 1.99f) in roundRect)
}
@Test
@@ -56,14 +56,14 @@
bottomLeft = CornerRadius.Zero
)
- assertFalse(roundRect.contains(Offset(1.0f, 1.0f)))
- assertFalse(roundRect.contains(Offset(1.1f, 1.1f)))
- assertTrue(roundRect.contains(Offset(1.15f, 1.15f)))
- assertFalse(roundRect.contains(Offset(2.0f, 1.0f)))
- assertFalse(roundRect.contains(Offset(1.93f, 1.07f)))
- assertFalse(roundRect.contains(Offset(1.97f, 1.7f)))
- assertTrue(roundRect.contains(Offset(1.7f, 1.97f)))
- assertTrue(roundRect.contains(Offset(1.0f, 1.99f)))
+ assertFalse(Offset(1.0f, 1.0f) in roundRect)
+ assertFalse(Offset(1.1f, 1.1f) in roundRect)
+ assertTrue(Offset(1.15f, 1.15f) in roundRect)
+ assertFalse(Offset(2.0f, 1.0f) in roundRect)
+ assertFalse(Offset(1.93f, 1.07f) in roundRect)
+ assertFalse(Offset(1.97f, 1.7f) in roundRect)
+ assertTrue(Offset(1.7f, 1.97f) in roundRect)
+ assertTrue(Offset(1.0f, 1.99f) in roundRect)
}
@Test
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 8ea7c85..5adc1c1 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -220,6 +220,7 @@
assertTrue(info.isHeading)
assertTrue(info.isClickable)
assertTrue(info.isVisibleToUser)
+ assertTrue(info.isImportantForAccessibility)
}
@Test
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index 4775e3a..ad153c9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -431,6 +431,12 @@
info.packageName = view.context.packageName
+ // This property exists to distinguish semantically meaningful nodes from purely structural
+ // or decorative UI elements. In Compose, LayoutNodes without semantics are simply omitted
+ // from the AccessibilityNodeInfo tree. Therefore, every AccessibilityNodeInfo qualifies as
+ // "important".
+ info.isImportantForAccessibility = true
+
semanticsNode.replacedChildrenSortedByBounds.fastForEach { child ->
if (currentSemanticsNodes.contains(child.id)) {
val holder = view.androidViewsHandler.layoutNodeToHolder[child.layoutNode]
diff --git a/core/core-ktx/api/current.txt b/core/core-ktx/api/current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/current.txt
+++ b/core/core-ktx/api/current.txt
@@ -563,6 +563,7 @@
method public static inline boolean isNotEmpty(android.view.Menu);
method public static operator java.util.Iterator iterator(android.view.Menu);
method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+ method public static inline void removeItemAt(android.view.Menu, int index);
}
public final class ViewGroupKt {
diff --git a/core/core-ktx/api/public_plus_experimental_current.txt b/core/core-ktx/api/public_plus_experimental_current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/public_plus_experimental_current.txt
+++ b/core/core-ktx/api/public_plus_experimental_current.txt
@@ -563,6 +563,7 @@
method public static inline boolean isNotEmpty(android.view.Menu);
method public static operator java.util.Iterator iterator(android.view.Menu);
method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+ method public static inline void removeItemAt(android.view.Menu, int index);
}
public final class ViewGroupKt {
diff --git a/core/core-ktx/api/restricted_current.txt b/core/core-ktx/api/restricted_current.txt
index 521f26c..6434f94 100644
--- a/core/core-ktx/api/restricted_current.txt
+++ b/core/core-ktx/api/restricted_current.txt
@@ -563,6 +563,7 @@
method public static inline boolean isNotEmpty(android.view.Menu);
method public static operator java.util.Iterator iterator(android.view.Menu);
method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+ method public static inline void removeItemAt(android.view.Menu, int index);
}
public final class ViewGroupKt {
diff --git a/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt b/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
index 6e195ce..9d5482a 100644
--- a/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
+++ b/core/core-ktx/src/androidTest/java/androidx/core/view/MenuTest.kt
@@ -136,8 +136,8 @@
}
@Test fun iteratorRemoving() {
- val item1 = menu.add("")
- val item2 = menu.add("")
+ val item1 = menu.add(NONE, 9, NONE, "")
+ val item2 = menu.add(NONE, 13, NONE, "")
val iterator = menu.iterator()
@@ -163,4 +163,17 @@
assertSame(items[index], child)
}
}
+
+ @Test fun removeItemAt() {
+ val item1 = menu.add(NONE, 9, NONE, "")
+ val item2 = menu.add(NONE, 13, NONE, "")
+
+ menu.removeItemAt(0)
+ assertFalse(item1 in menu)
+ assertEquals(1, menu.size())
+
+ menu.removeItemAt(0)
+ assertFalse(item2 in menu)
+ assertEquals(0, menu.size())
+ }
}
diff --git a/core/core-ktx/src/main/java/androidx/core/view/Menu.kt b/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
index 804c64b..91095ce 100644
--- a/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
+++ b/core/core-ktx/src/main/java/androidx/core/view/Menu.kt
@@ -71,9 +71,17 @@
private var index = 0
override fun hasNext() = index < size()
override fun next() = getItem(index++) ?: throw IndexOutOfBoundsException()
- override fun remove() = removeItem(--index)
+ override fun remove() = removeItemAt(--index)
}
+/**
+ * Removes the menu item at the specified index.
+ *
+ * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the count.
+ */
+public inline fun Menu.removeItemAt(index: Int) =
+ getItem(index)?.let { removeItem(it.itemId) } ?: throw IndexOutOfBoundsException()
+
/** Returns a [Sequence] over the items in this menu. */
public val Menu.children: Sequence
get() = object : Sequence
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 45e2858..f3eac5e 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -238,6 +238,7 @@
Found an unresolved type in androidx\.slice\.builders\$list\(android\.content\.Context, android\.net\.Uri, kotlin\.Long, kotlin\.Function[0-9]+\(\(androidx\.slice\.builders\.ListBuilderDsl, kotlin\.Unit\)\)\) \(ListBuilder\.kt:[0-9]+\)
# See b/180023439 for hiltNavGraphViewModel warning.
Unresolved link to .*
+Found an unresolved type in androidx\.core\.view\$removeItemAt\(android\.view\.Menu, kotlin\.Int\) \(Menu\.kt:[0-9]+\)
Found an unresolved type in androidx\.compose\.foundation\.MutatorMutex\$mutate\(androidx\.compose\.foundation\.MutatePriority, kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.MutatorMutex\.mutate\.R\)\)\) \(MutatorMutex\.kt:[0-9]+\)
Found an unresolved type in androidx\.compose\.foundation\.MutatorMutex\$mutateWith\(androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.T, androidx\.compose\.foundation\.MutatePriority, kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.T, androidx\.compose\.foundation\.MutatorMutex\.mutateWith\.R\)\)\) \(MutatorMutex\.kt:[0-9]+\)
Found an unresolved type in androidx\.compose\.foundation\.gestures\$detectTapGestures\(androidx\.compose\.ui\.input\.pointer\.PointerInputScope, kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.coroutines\.SuspendFunction[0-9]+\(\(androidx\.compose\.foundation\.gestures\.PressGestureScope, androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\), kotlin\.Function[0-9]+\(\(androidx\.compose\.ui\.geometry\.Offset, kotlin\.Unit\)\)\) \(TapGestureDetector\.kt:[0-9]+\)
@@ -432,8 +433,7 @@
# Linux builds cannot build mac, hence we get this warning.
# see: https://github.com/JetBrains/kotlin/blob/master/native/commonizer/README.md
# This warning is printed from: https://github.com/JetBrains/kotlin/blob/bc853e45e8982eff74e3263b0197c1af6086615d/native/commonizer/src/org/jetbrains/kotlin/commonizer/konan/LibraryCommonizer.kt#L41
-Warning\: No libraries found for target macos_arm[0-9]+\. This target will be excluded from commonization\.
-Warning\: No libraries found for target macos_x[0-9]+\. This target will be excluded from commonization\.
+Warning\: No libraries found for target (macos|ios|ios_simulator)_(arm|x)[0-9]+\. This target will be excluded from commonization\.
# > Task :tv:tv-foundation:processDebugManifest
# > Task :tv:tv-material:processDebugManifest
# > Task :tv:tv-material:processReleaseManifest
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 630cc0f..a707641 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -30,11 +30,11 @@
guavaJre = "31.1-jre"
hilt = "2.42"
incap = "0.2"
-kotlin = "1.7.0"
-kotlinNative = "1.7.0"
+kotlin = "1.7.10"
+kotlinNative = "1.7.10"
kotlinCompileTesting = "1.4.1"
kotlinCoroutines = "1.6.1"
-ksp = "1.7.0-1.0.6"
+ksp = "1.7.10-1.0.6"
ktlint = "0.46.0-20220520.192227-74"
leakcanary = "2.8.1"
metalava = "1.0.0-alpha06"
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 0cbbb70..ea6f9642 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1238,21 +1238,21 @@
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
diff --git a/health/health-connect-client/api/current.txt b/health/health-connect-client/api/current.txt
index 57d6ee29..a6a0214 100644
--- a/health/health-connect-client/api/current.txt
+++ b/health/health-connect-client/api/current.txt
@@ -162,7 +162,7 @@
property public androidx.health.connect.client.records.metadata.Metadata metadata;
property public java.time.Instant time;
property public java.time.ZoneOffset? zoneOffset;
- field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+ field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
}
@@ -323,26 +323,18 @@
field public static final String MEDIUM = "medium";
}
- public final class CyclingPedalingCadence {
- ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
- method public double getRevolutionsPerMinute();
- method public java.time.Instant getTime();
- property public final double revolutionsPerMinute;
- property public final java.time.Instant time;
- }
-
public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.Record {
- ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
public static final class CyclingPedalingCadenceRecord.Companion {
}
+ public static final class CyclingPedalingCadenceRecord.Sample {
+ ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+ method public double getRevolutionsPerMinute();
+ method public java.time.Instant getTime();
+ property public final double revolutionsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class DistanceRecord implements androidx.health.connect.client.records.Record {
ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
public static final class FloorsClimbedRecord.Companion {
}
- public final class HeartRate {
- ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
- method public long getBeatsPerMinute();
- method public java.time.Instant getTime();
- property public final long beatsPerMinute;
- property public final java.time.Instant time;
- }
-
public final class HeartRateRecord implements androidx.health.connect.client.records.Record {
- ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
public static final class HeartRateRecord.Companion {
}
+ public static final class HeartRateRecord.Sample {
+ ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+ method public long getBeatsPerMinute();
+ method public java.time.Instant getTime();
+ property public final long beatsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class HeightRecord implements androidx.health.connect.client.records.Record {
ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getHeight();
@@ -1099,26 +1099,18 @@
property public final java.time.Instant time;
}
- public final class StepsCadence {
- ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
- method public double getRate();
- method public java.time.Instant getTime();
- property public final double rate;
- property public final java.time.Instant time;
- }
-
public final class StepsCadenceRecord implements androidx.health.connect.client.records.Record {
- ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1130,6 +1122,14 @@
public static final class StepsCadenceRecord.Companion {
}
+ public static final class StepsCadenceRecord.Sample {
+ ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+ method public double getRate();
+ method public java.time.Instant getTime();
+ property public final double rate;
+ property public final java.time.Instant time;
+ }
+
public final class StepsRecord implements androidx.health.connect.client.records.Record {
ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public long getCount();
diff --git a/health/health-connect-client/api/public_plus_experimental_current.txt b/health/health-connect-client/api/public_plus_experimental_current.txt
index 57d6ee29..a6a0214 100644
--- a/health/health-connect-client/api/public_plus_experimental_current.txt
+++ b/health/health-connect-client/api/public_plus_experimental_current.txt
@@ -162,7 +162,7 @@
property public androidx.health.connect.client.records.metadata.Metadata metadata;
property public java.time.Instant time;
property public java.time.ZoneOffset? zoneOffset;
- field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+ field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
}
@@ -323,26 +323,18 @@
field public static final String MEDIUM = "medium";
}
- public final class CyclingPedalingCadence {
- ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
- method public double getRevolutionsPerMinute();
- method public java.time.Instant getTime();
- property public final double revolutionsPerMinute;
- property public final java.time.Instant time;
- }
-
public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.Record {
- ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
public static final class CyclingPedalingCadenceRecord.Companion {
}
+ public static final class CyclingPedalingCadenceRecord.Sample {
+ ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+ method public double getRevolutionsPerMinute();
+ method public java.time.Instant getTime();
+ property public final double revolutionsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class DistanceRecord implements androidx.health.connect.client.records.Record {
ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
public static final class FloorsClimbedRecord.Companion {
}
- public final class HeartRate {
- ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
- method public long getBeatsPerMinute();
- method public java.time.Instant getTime();
- property public final long beatsPerMinute;
- property public final java.time.Instant time;
- }
-
public final class HeartRateRecord implements androidx.health.connect.client.records.Record {
- ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
public static final class HeartRateRecord.Companion {
}
+ public static final class HeartRateRecord.Sample {
+ ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+ method public long getBeatsPerMinute();
+ method public java.time.Instant getTime();
+ property public final long beatsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class HeightRecord implements androidx.health.connect.client.records.Record {
ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getHeight();
@@ -1099,26 +1099,18 @@
property public final java.time.Instant time;
}
- public final class StepsCadence {
- ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
- method public double getRate();
- method public java.time.Instant getTime();
- property public final double rate;
- property public final java.time.Instant time;
- }
-
public final class StepsCadenceRecord implements androidx.health.connect.client.records.Record {
- ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1130,6 +1122,14 @@
public static final class StepsCadenceRecord.Companion {
}
+ public static final class StepsCadenceRecord.Sample {
+ ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+ method public double getRate();
+ method public java.time.Instant getTime();
+ property public final double rate;
+ property public final java.time.Instant time;
+ }
+
public final class StepsRecord implements androidx.health.connect.client.records.Record {
ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public long getCount();
diff --git a/health/health-connect-client/api/restricted_current.txt b/health/health-connect-client/api/restricted_current.txt
index 27ca15d..9dd1f89 100644
--- a/health/health-connect-client/api/restricted_current.txt
+++ b/health/health-connect-client/api/restricted_current.txt
@@ -162,7 +162,7 @@
property public androidx.health.connect.client.records.metadata.Metadata metadata;
property public java.time.Instant time;
property public java.time.ZoneOffset? zoneOffset;
- field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
+ field public static final androidx.health.connect.client.aggregate.AggregateMetric BASAL_CALORIES_TOTAL;
field public static final androidx.health.connect.client.records.BasalMetabolicRateRecord.Companion Companion;
}
@@ -323,26 +323,18 @@
field public static final String MEDIUM = "medium";
}
- public final class CyclingPedalingCadence {
- ctor public CyclingPedalingCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
- method public double getRevolutionsPerMinute();
- method public java.time.Instant getTime();
- property public final double revolutionsPerMinute;
- property public final java.time.Instant time;
- }
-
- public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
- ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ public final class CyclingPedalingCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
+ ctor public CyclingPedalingCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.CyclingPedalingCadenceRecord.Companion Companion;
@@ -354,6 +346,14 @@
public static final class CyclingPedalingCadenceRecord.Companion {
}
+ public static final class CyclingPedalingCadenceRecord.Sample {
+ ctor public CyclingPedalingCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double revolutionsPerMinute);
+ method public double getRevolutionsPerMinute();
+ method public java.time.Instant getTime();
+ property public final double revolutionsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class DistanceRecord implements androidx.health.connect.client.records.IntervalRecord {
ctor public DistanceRecord(androidx.health.connect.client.units.Length distance, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getDistance();
@@ -620,26 +620,18 @@
public static final class FloorsClimbedRecord.Companion {
}
- public final class HeartRate {
- ctor public HeartRate(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
- method public long getBeatsPerMinute();
- method public java.time.Instant getTime();
- property public final long beatsPerMinute;
- property public final java.time.Instant time;
- }
-
- public final class HeartRateRecord implements androidx.health.connect.client.records.SeriesRecord {
- ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ public final class HeartRateRecord implements androidx.health.connect.client.records.SeriesRecord {
+ ctor public HeartRateRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.aggregate.AggregateMetric BPM_AVG;
@@ -652,6 +644,14 @@
public static final class HeartRateRecord.Companion {
}
+ public static final class HeartRateRecord.Sample {
+ ctor public HeartRateRecord.Sample(java.time.Instant time, @IntRange(from=1L, to=300L) long beatsPerMinute);
+ method public long getBeatsPerMinute();
+ method public java.time.Instant getTime();
+ property public final long beatsPerMinute;
+ property public final java.time.Instant time;
+ }
+
public final class HeightRecord implements androidx.health.connect.client.records.InstantaneousRecord {
ctor public HeightRecord(androidx.health.connect.client.units.Length height, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public androidx.health.connect.client.units.Length getHeight();
@@ -1122,26 +1122,18 @@
property public final java.time.Instant time;
}
- public final class StepsCadence {
- ctor public StepsCadence(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
- method public double getRate();
- method public java.time.Instant getTime();
- property public final double rate;
- property public final java.time.Instant time;
- }
-
- public final class StepsCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
- ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+ public final class StepsCadenceRecord implements androidx.health.connect.client.records.SeriesRecord {
+ ctor public StepsCadenceRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List samples, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public java.time.Instant getEndTime();
method public java.time.ZoneOffset? getEndZoneOffset();
method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
- method public java.util.List getSamples();
+ method public java.util.List getSamples();
method public java.time.Instant getStartTime();
method public java.time.ZoneOffset? getStartZoneOffset();
property public java.time.Instant endTime;
property public java.time.ZoneOffset? endZoneOffset;
property public androidx.health.connect.client.records.metadata.Metadata metadata;
- property public java.util.List samples;
+ property public java.util.List samples;
property public java.time.Instant startTime;
property public java.time.ZoneOffset? startZoneOffset;
field public static final androidx.health.connect.client.records.StepsCadenceRecord.Companion Companion;
@@ -1153,6 +1145,14 @@
public static final class StepsCadenceRecord.Companion {
}
+ public static final class StepsCadenceRecord.Sample {
+ ctor public StepsCadenceRecord.Sample(java.time.Instant time, @FloatRange(from=0.0, to=10000.0) double rate);
+ method public double getRate();
+ method public java.time.Instant getTime();
+ property public final double rate;
+ property public final java.time.Instant time;
+ }
+
public final class StepsRecord implements androidx.health.connect.client.records.IntervalRecord {
ctor public StepsRecord(long count, java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
method public long getCount();
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index f767c8b..41d9897 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -28,7 +28,6 @@
import androidx.health.connect.client.records.BodyWaterMassRecord
import androidx.health.connect.client.records.BoneMassRecord
import androidx.health.connect.client.records.CervicalMucusRecord
-import androidx.health.connect.client.records.CyclingPedalingCadence
import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
import androidx.health.connect.client.records.DistanceRecord
import androidx.health.connect.client.records.ElevationGainedRecord
@@ -37,7 +36,6 @@
import androidx.health.connect.client.records.ExerciseRepetitionsRecord
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.FloorsClimbedRecord
-import androidx.health.connect.client.records.HeartRate
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.HeartRateVariabilityDifferentialIndexRecord
import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
@@ -64,7 +62,6 @@
import androidx.health.connect.client.records.SleepSessionRecord
import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SpeedRecord
-import androidx.health.connect.client.records.StepsCadence
import androidx.health.connect.client.records.StepsCadenceRecord
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.SwimmingStrokesRecord
@@ -171,7 +168,7 @@
endZoneOffset = endZoneOffset,
samples =
seriesValuesList.map { value ->
- CyclingPedalingCadence(
+ CyclingPedalingCadenceRecord.Sample(
time = Instant.ofEpochMilli(value.instantTimeMillis),
revolutionsPerMinute = value.getDouble("rpm"),
)
@@ -186,7 +183,7 @@
endZoneOffset = endZoneOffset,
samples =
seriesValuesList.map { value ->
- HeartRate(
+ HeartRateRecord.Sample(
time = Instant.ofEpochMilli(value.instantTimeMillis),
beatsPerMinute = value.getLong("bpm"),
)
@@ -357,7 +354,7 @@
endZoneOffset = endZoneOffset,
samples =
seriesValuesList.map { value ->
- StepsCadence(
+ StepsCadenceRecord.Sample(
time = Instant.ofEpochMilli(value.instantTimeMillis),
rate = value.getDouble("rate"),
)
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
index 156351e..4550a2b 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
@@ -17,6 +17,7 @@
import androidx.health.connect.client.aggregate.AggregateMetric
import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Energy
import androidx.health.connect.client.units.Power
import java.time.Instant
import java.time.ZoneOffset
@@ -66,12 +67,12 @@
* [androidx.health.connect.client.aggregate.AggregationResult].
*/
@JvmField
- val BASAL_CALORIES_TOTAL: AggregateMetric =
+ val BASAL_CALORIES_TOTAL: AggregateMetric =
AggregateMetric.doubleMetric(
dataTypeName = BASAL_CALORIES_TYPE_NAME,
aggregationType = AggregateMetric.AggregationType.TOTAL,
fieldName = ENERGY_FIELD_NAME,
- mapper = Power::kilocaloriesPerDay,
+ mapper = Energy::calories,
)
}
}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
index c8ba7be..0c87da2 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
@@ -33,9 +33,9 @@
override val startZoneOffset: ZoneOffset?,
override val endTime: Instant,
override val endZoneOffset: ZoneOffset?,
- override val samples: List,
+ override val samples: List,
override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
/*
* Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -89,44 +89,44 @@
*/
@JvmField val RPM_MAX: AggregateMetric = doubleMetric(TYPE, MAXIMUM, RPM_FIELD)
}
-}
-/**
- * Represents a single measurement of the cycling pedaling cadence.
- *
- * @param time The point in time when the measurement was taken.
- * @param revolutionsPerMinute Cycling revolutions per minute. Valid range: 0-10000.
- *
- * @see CyclingPedalingCadenceRecord
- */
-public class CyclingPedalingCadence(
- val time: Instant,
- @FloatRange(from = 0.0, to = 10_000.0) val revolutionsPerMinute: Double,
-) {
-
- init {
- requireNonNegative(value = revolutionsPerMinute, name = "revolutionsPerMinute")
- }
-
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ /**
+ * Represents a single measurement of the cycling pedaling cadence.
+ *
+ * @param time The point in time when the measurement was taken.
+ * @param revolutionsPerMinute Cycling revolutions per minute. Valid range: 0-10000.
+ *
+ * @see CyclingPedalingCadenceRecord
*/
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is CyclingPedalingCadence) return false
+ public class Sample(
+ val time: Instant,
+ @FloatRange(from = 0.0, to = 10_000.0) val revolutionsPerMinute: Double,
+ ) {
- if (time != other.time) return false
- if (revolutionsPerMinute != other.revolutionsPerMinute) return false
+ init {
+ requireNonNegative(value = revolutionsPerMinute, name = "revolutionsPerMinute")
+ }
- return true
- }
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Sample) return false
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
- */
- override fun hashCode(): Int {
- var result = time.hashCode()
- result = 31 * result + revolutionsPerMinute.hashCode()
- return result
+ if (time != other.time) return false
+ if (revolutionsPerMinute != other.revolutionsPerMinute) return false
+
+ return true
+ }
+
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun hashCode(): Int {
+ var result = time.hashCode()
+ result = 31 * result + revolutionsPerMinute.hashCode()
+ return result
+ }
}
}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
index b16f820..bd488fe 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
@@ -27,9 +27,9 @@
override val startZoneOffset: ZoneOffset?,
override val endTime: Instant,
override val endZoneOffset: ZoneOffset?,
- override val samples: List,
+ override val samples: List,
override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
/*
* Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -100,44 +100,44 @@
val MEASUREMENTS_COUNT: AggregateMetric =
AggregateMetric.countMetric(HEART_RATE_TYPE_NAME)
}
-}
-/**
- * Represents a single measurement of the heart rate.
- *
- * @param time The point in time when the measurement was taken.
- * @param beatsPerMinute Heart beats per minute. Validation range: 1-300.
- *
- * @see HeartRateRecord
- */
-public class HeartRate(
- val time: Instant,
- @androidx.annotation.IntRange(from = 1, to = 300) val beatsPerMinute: Long,
-) {
-
- init {
- requireNonNegative(value = beatsPerMinute, name = "beatsPerMinute")
- }
-
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ /**
+ * Represents a single measurement of the heart rate.
+ *
+ * @param time The point in time when the measurement was taken.
+ * @param beatsPerMinute Heart beats per minute. Validation range: 1-300.
+ *
+ * @see HeartRateRecord
*/
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is HeartRate) return false
+ public class Sample(
+ val time: Instant,
+ @androidx.annotation.IntRange(from = 1, to = 300) val beatsPerMinute: Long,
+ ) {
- if (time != other.time) return false
- if (beatsPerMinute != other.beatsPerMinute) return false
+ init {
+ requireNonNegative(value = beatsPerMinute, name = "beatsPerMinute")
+ }
- return true
- }
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Sample) return false
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
- */
- override fun hashCode(): Int {
- var result = time.hashCode()
- result = 31 * result + beatsPerMinute.hashCode()
- return result
+ if (time != other.time) return false
+ if (beatsPerMinute != other.beatsPerMinute) return false
+
+ return true
+ }
+
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun hashCode(): Int {
+ var result = time.hashCode()
+ result = 31 * result + beatsPerMinute.hashCode()
+ return result
+ }
}
}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
index c4a2852..40299d1 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
@@ -20,9 +20,13 @@
/** Type of meal. */
public object MealType {
const val UNKNOWN = "unknown"
+ /** Use this for the first meal of the day, usually the morning meal. */
const val BREAKFAST = "breakfast"
+ /** Use this for the noon meal. */
const val LUNCH = "lunch"
+ /** Use this for last meal of the day, usually the evening meal. */
const val DINNER = "dinner"
+ /** Any meal outside of the usual three meals per day. */
const val SNACK = "snack"
}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
index b10ccfe..d7488b6 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
@@ -31,9 +31,9 @@
override val startZoneOffset: ZoneOffset?,
override val endTime: Instant,
override val endZoneOffset: ZoneOffset?,
- override val samples: List,
+ override val samples: List,
override val metadata: Metadata = Metadata.EMPTY,
-) : SeriesRecord {
+) : SeriesRecord {
/*
* Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
@@ -87,42 +87,42 @@
*/
@JvmField val RATE_MAX: AggregateMetric = doubleMetric(TYPE, MAXIMUM, RATE_FIELD)
}
-}
-/**
- * Represents a single measurement of the steps cadence.
- *
- * @param time The point in time when the measurement was taken.
- * @param rate Rate in steps per minute. Valid range: 0-10000.
- */
-class StepsCadence(
- val time: Instant,
- @FloatRange(from = 0.0, to = 10_000.0) val rate: Double,
-) {
-
- init {
- requireNonNegative(value = rate, name = "rate")
- }
-
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ /**
+ * Represents a single measurement of the steps cadence.
+ *
+ * @param time The point in time when the measurement was taken.
+ * @param rate Rate in steps per minute. Valid range: 0-10000.
*/
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is StepsCadence) return false
+ class Sample(
+ val time: Instant,
+ @FloatRange(from = 0.0, to = 10_000.0) val rate: Double,
+ ) {
- if (time != other.time) return false
- if (rate != other.rate) return false
+ init {
+ requireNonNegative(value = rate, name = "rate")
+ }
- return true
- }
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Sample) return false
- /*
- * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
- */
- override fun hashCode(): Int {
- var result = time.hashCode()
- result = 31 * result + rate.hashCode()
- return result
+ if (time != other.time) return false
+ if (rate != other.rate) return false
+
+ return true
+ }
+
+ /*
+ * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+ */
+ override fun hashCode(): Int {
+ var result = time.hashCode()
+ result = 31 * result + rate.hashCode()
+ return result
+ }
}
}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
index 8c09819..cb606a9 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByDurationRequest.kt
@@ -24,6 +24,10 @@
* Request object to read time bucketed aggregations for given [AggregateMetric]s in Android Health
* Platform.
*
+ * [timeRangeSlicer] contains a [Duration] of fixed physical time intervals, such as per hour, per
+ * ten minutes or so. Prefer [AggregateGroupByPeriodRequest], if you would like variable length time
+ * intervals, such as per day, which may or may not include DST (23 or 25 hour).
+ *
* @param metrics Set of [AggregateMetric]s to aggregate, such as `Steps::STEPS_COUNT_TOTAL`.
* @param timeRangeFilter The [TimeRangeFilter] to read from.
* @param timeRangeSlicer The bucket size of each returned aggregate row. [timeRangeFilter] will be
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
index bae6286..00ca4b0 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/AggregateGroupByPeriodRequest.kt
@@ -24,6 +24,10 @@
* Request object to read time bucketed aggregations for given [AggregateMetric]s in Android Health
* Platform.
*
+ * [timeRangeSlicer] contains a [Period] of variable length time intervals, such as per day, which
+ * may be anywhere between 23, 24, or 25 hour. Use [AggregateGroupByDurationRequest] if time slice
+ * is of fixed intervals, such as an hour, every ten minutes.
+ *
* @param metrics Set of [AggregateMetric]s to aggregate, such as `Steps::STEPS_COUNT_TOTAL`.
* @param timeRangeFilter The [TimeRangeFilter] to read from.
* @param timeRangeSlicer The bucket size of each returned aggregate row. [timeRangeFilter] will be
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
index ec02294..9df3d3c 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/HealthConnectClientTest.kt
@@ -22,10 +22,9 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
-import java.lang.IllegalStateException
-import java.lang.UnsupportedOperationException
import org.junit.Assert.assertThrows
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Shadows
@@ -43,6 +42,7 @@
context = ApplicationProvider.getApplicationContext()
}
+ @Ignore // b/238635208
@Test
fun noBackingImplementation_unavailable() {
val packageManager = context.packageManager
@@ -54,6 +54,7 @@
}
}
+ @Ignore // b/238635208
@Test
fun backingImplementation_notEnabled_unavailable() {
installPackage(context, PROVIDER_PACKAGE_NAME, enabled = false)
@@ -64,6 +65,7 @@
}
}
+ @Ignore // b/238635208
@Test
fun backingImplementation_enabled_isAvailable() {
installPackage(context, PROVIDER_PACKAGE_NAME, enabled = true)
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index 2679086d..1510f7b 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -29,7 +29,6 @@
import androidx.health.connect.client.records.CervicalMucusRecord
import androidx.health.connect.client.records.CervicalMucusRecord.Appearance
import androidx.health.connect.client.records.CervicalMucusRecord.Sensation
-import androidx.health.connect.client.records.CyclingPedalingCadence
import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
import androidx.health.connect.client.records.DistanceRecord
import androidx.health.connect.client.records.ElevationGainedRecord
@@ -40,7 +39,6 @@
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.ExerciseSessionRecord.ExerciseType
import androidx.health.connect.client.records.FloorsClimbedRecord
-import androidx.health.connect.client.records.HeartRate
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.HeartRateVariabilityDifferentialIndexRecord
import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
@@ -70,7 +68,6 @@
import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SleepStageRecord.StageType
import androidx.health.connect.client.records.SpeedRecord
-import androidx.health.connect.client.records.StepsCadence
import androidx.health.connect.client.records.StepsCadenceRecord
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.SwimmingStrokesRecord
@@ -277,11 +274,11 @@
endZoneOffset = END_ZONE_OFFSET,
samples =
listOf(
- CyclingPedalingCadence(
+ CyclingPedalingCadenceRecord.Sample(
time = START_TIME,
revolutionsPerMinute = 1.0,
),
- CyclingPedalingCadence(
+ CyclingPedalingCadenceRecord.Sample(
time = START_TIME,
revolutionsPerMinute = 2.0,
),
@@ -303,15 +300,15 @@
endZoneOffset = END_ZONE_OFFSET,
samples =
listOf(
- HeartRate(
+ HeartRateRecord.Sample(
time = START_TIME,
beatsPerMinute = 100L,
),
- HeartRate(
+ HeartRateRecord.Sample(
time = START_TIME,
beatsPerMinute = 110L,
),
- HeartRate(
+ HeartRateRecord.Sample(
time = START_TIME,
beatsPerMinute = 120L,
),
@@ -637,11 +634,11 @@
endZoneOffset = END_ZONE_OFFSET,
samples =
listOf(
- StepsCadence(
+ StepsCadenceRecord.Sample(
time = START_TIME,
rate = 1.0,
),
- StepsCadence(
+ StepsCadenceRecord.Sample(
time = START_TIME,
rate = 2.0,
),
diff --git a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
index b295712..dd49758 100644
--- a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/data/ProtoParcelableTest.kt
@@ -21,6 +21,7 @@
import androidx.health.platform.client.proto.BytesValue
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,6 +37,7 @@
assertThat(parcelAndRead(protoParcelable).proto).isEqualTo(protoParcelable.proto)
}
+ @Ignore // b/238635208
@Test
fun storeInSharedMemory() {
// Big enough that it will be stored in shared memory.
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index f6f7ca1..7153d0f 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,7 +25,7 @@
kotlin.code.style=official
# Disable docs
androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=8757744
+androidx.playground.snapshotBuildId=8818165
androidx.playground.metalavaBuildId=8670428
androidx.playground.dokkaBuildId=7472101
androidx.studio.type=playground
diff --git a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
index 34f094d..88efeb2 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
+++ b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
@@ -64,6 +64,7 @@
import com.google.common.truth.Truth;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -1101,6 +1102,7 @@
assertThat(output).isEqualTo(Color.WHITE);
}
+ @Ignore // b/238635208
@Test
public void placeholderIcon() {
mComplicationRenderer.setComplicationData(
@@ -1112,6 +1114,7 @@
assertThat(mComplicationRenderer.mIsPlaceholderIcon).isTrue();
}
+ @Ignore // b/238635208
@Test
public void placeholderSmallImage() {
mComplicationRenderer.setComplicationData(
@@ -1123,6 +1126,7 @@
assertThat(mComplicationRenderer.mIsPlaceholderSmallImage).isTrue();
}
+ @Ignore // b/238635208
@Test
public void placeholderPhotoImage() {
mComplicationRenderer.setComplicationData(
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
index be20dbb..0d1a4e8 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Assert.assertThrows
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -900,6 +901,7 @@
assertThat(digestHash1).isNotEqualTo(digestHash2)
}
+ @Ignore // b/238635208
@Test
public fun digestHashSensitiveToIconChanges() {
val colorStyleSetting1 = ListUserStyleSetting(
diff --git a/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java b/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
index 7648800..c31650f 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/BackButtonDismissController.java
@@ -20,13 +20,12 @@
import android.view.KeyEvent;
import android.view.animation.Animation;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.UiThread;
import androidx.wear.utils.ActivityAnimationUtil;
-import org.jetbrains.annotations.NotNull;
-
/**
* Controller that handles the back button click for dismiss the frame layout
*
@@ -49,7 +48,7 @@
&& dismiss());
}
- void disable(@NotNull DismissibleFrameLayout layout) {
+ void disable(@NonNull DismissibleFrameLayout layout) {
setOnDismissListener(null);
layout.setOnKeyListener(null);
// setting this to false will also ensure that this view is not focusable in touch mode
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
index f657cc2..23d44ca 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
@@ -167,4 +167,22 @@
Assert.assertEquals("androidx.webkit.test", headerValue);
}
}
+
+ @Test
+ public void testEnterpriseAuthenticationAppLinkPolicyEnabled() throws Throwable {
+ WebkitUtils.checkFeature(WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY);
+
+ assertTrue(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+ mWebViewOnUiThread.getSettings()));
+
+ WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(
+ mWebViewOnUiThread.getSettings(), false);
+ assertFalse(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+ mWebViewOnUiThread.getSettings()));
+
+ WebSettingsCompat.setEnterpriseAuthenticationAppLinkPolicyEnabled(
+ mWebViewOnUiThread.getSettings(), true);
+ assertTrue(WebSettingsCompat.getEnterpriseAuthenticationAppLinkPolicyEnabled(
+ mWebViewOnUiThread.getSettings()));
+ }
}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 477c312e..9e97fc8 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -724,6 +724,65 @@
}
}
+ /**
+ * Sets whether EnterpriseAuthenticationAppLinkPolicy if set by admin is allowed to have any
+ * effect on WebView.
+ *
+ * EnterpriseAuthenticationAppLinkPolicy in WebView allows admins to specify authentication
+ * urls. When WebView is redirected to authentication url, and an app on the device has
+ * registered as the default handler for the url, that app is launched.
+ *
+ * EnterpriseAuthenticationAppLinkPolicy is enabled by default.
+ *
+ *
+ * This method should only be called if
+ * {@link WebViewFeature#isFeatureSupported(String)}
+ * returns true for {@link WebViewFeature#ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY}.
+ *
+ * @param enabled Whether EnterpriseAuthenticationAppLinkPolicy should be enabled.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @RequiresFeature(name = WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
+ enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+ public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(
+ @NonNull WebSettings settings,
+ boolean enabled) {
+ ApiFeature.NoFramework feature =
+ WebViewFeatureInternal.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY;
+ if (feature.isSupportedByWebView()) {
+ getAdapter(settings).setEnterpriseAuthenticationAppLinkPolicyEnabled(enabled);
+ } else {
+ throw WebViewFeatureInternal.getUnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Gets whether EnterpriseAuthenticationAppLinkPolicy is allowed to have any effect on WebView.
+ *
+ *
+ * This method should only be called if
+ * {@link WebViewFeature#isFeatureSupported(String)}
+ * returns true for {@link WebViewFeature#ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY}.
+ *
+ * @return {@code true} if EnterpriseAuthenticationAppLinkPolicy is enabled and {@code false}
+ * otherwise.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @RequiresFeature(name = WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
+ enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+ public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(
+ @NonNull WebSettings settings) {
+ ApiFeature.NoFramework feature =
+ WebViewFeatureInternal.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY;
+ if (feature.isSupportedByWebView()) {
+ return getAdapter(settings).getEnterpriseAuthenticationAppLinkPolicyEnabled();
+ } else {
+ throw WebViewFeatureInternal.getUnsupportedOperationException();
+ }
+ }
+
private static WebSettingsAdapter getAdapter(WebSettings settings) {
return WebViewGlueCommunicator.getCompatConverter().convertSettings(settings);
}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 5de5148..8d50e6c 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -98,6 +98,7 @@
GET_VARIATIONS_HEADER,
ALGORITHMIC_DARKENING,
REQUESTED_WITH_HEADER_CONTROL,
+ ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -492,6 +493,18 @@
public static final String REQUESTED_WITH_HEADER_CONTROL = "REQUESTED_WITH_HEADER_CONTROL";
/**
+ * Feature for {@link #isFeatureSupported(String)}.
+ * This feature covers
+ * {@link WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)}and
+ * {@link WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings)}.
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
+ "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+
+ /**
* 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/WebSettingsAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
index 963fb99..8d66c85 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
@@ -140,6 +140,22 @@
/**
* Adapter method for
+ * {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled}.
+ */
+ public void setEnterpriseAuthenticationAppLinkPolicyEnabled(boolean enabled) {
+ mBoundaryInterface.setEnterpriseAuthenticationAppLinkPolicyEnabled(enabled);
+ }
+
+ /**
+ * Adapter method for
+ * {@link androidx.webkit.WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled}.
+ */
+ public boolean getEnterpriseAuthenticationAppLinkPolicyEnabled() {
+ return mBoundaryInterface.getEnterpriseAuthenticationAppLinkPolicyEnabled();
+ }
+
+ /**
+ * Adapter method for
* {@link androidx.webkit.WebSettingsCompat#getRequestedWithHeaderMode(android.webkit.WebSettings)}
*/
public int getRequestedWithHeaderMode() {
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 fc179e8..aa106c7 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -473,6 +473,15 @@
new ApiFeature.NoFramework(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
Features.REQUESTED_WITH_HEADER_CONTROL);
+
+ /**
+ * This feature covers
+ * {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)} and
+ * {@link androidx.webkit.WebSettingsCompat#getEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings)}.
+ */
+ 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);
// --- Add new feature constants above this line ---
private WebViewFeatureInternal() {
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
index c90bcc8..ea3ad1f 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
@@ -22,6 +22,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
import androidx.work.WorkInfo.State
import androidx.work.WorkManager.UpdateResult.APPLIED_FOR_NEXT_RUN
import androidx.work.WorkManager.UpdateResult.APPLIED_IMMEDIATELY
@@ -35,6 +36,8 @@
import androidx.work.impl.testutils.TestConstraintTracker
import androidx.work.impl.utils.taskexecutor.TaskExecutor
import androidx.work.impl.utils.taskexecutor.WorkManagerTaskExecutor
+import androidx.work.impl.workers.ARGUMENT_CLASS_NAME
+import androidx.work.impl.workers.ConstraintTrackingWorker
import androidx.work.worker.LatchWorker
import androidx.work.worker.RetryWorker
import androidx.work.worker.TestWorker
@@ -413,8 +416,10 @@
val spec = workManager.workDatabase.workSpecDao().getWorkSpec(request.stringId)!!
val delta = spec.calculateNextRunTime() - System.currentTimeMillis()
assertThat(delta).isGreaterThan(0)
- workManager.workDatabase.workSpecDao().setLastEnqueuedTime(request.stringId,
- spec.lastEnqueueTime - delta)
+ workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+ request.stringId,
+ spec.lastEnqueueTime - delta
+ )
val updated = OneTimeWorkRequest.Builder(TestWorker::class.java).setId(request.id)
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.DAYS)
.build()
@@ -431,8 +436,10 @@
.setInitialDelay(10, TimeUnit.MINUTES).build()
val enqueueTime = System.currentTimeMillis()
workManager.enqueue(request).result.get()
- workManager.workDatabase.workSpecDao().setLastEnqueuedTime(request.stringId,
- enqueueTime - TimeUnit.MINUTES.toMillis(5))
+ workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+ request.stringId,
+ enqueueTime - TimeUnit.MINUTES.toMillis(5)
+ )
val updated = OneTimeWorkRequest.Builder(TestWorker::class.java)
.setInitialDelay(20, TimeUnit.MINUTES)
.setId(request.id)
@@ -446,6 +453,24 @@
assertThat(delta).isLessThan(TimeUnit.MINUTES.toMillis(16))
}
+ @Test
+ @MediumTest
+ @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
+ fun testUpdatePeriodicWorker_preservesConstraintTrackingWorker() {
+ val originRequest = OneTimeWorkRequest.Builder(TestWorker::class.java)
+ .setInitialDelay(10, TimeUnit.HOURS).build()
+ workManager.enqueue(originRequest).result.get()
+ val updateRequest = OneTimeWorkRequest.Builder(RetryWorker::class.java)
+ .setId(originRequest.id).setInitialDelay(10, TimeUnit.HOURS)
+ .setConstraints(Constraints(requiresBatteryNotLow = true))
+ .build()
+ workManager.updateWork(updateRequest).get()
+ val workSpec = db.workSpecDao().getWorkSpec(originRequest.stringId)!!
+ assertThat(workSpec.workerClassName).isEqualTo(ConstraintTrackingWorker::class.java.name)
+ assertThat(workSpec.input.getString(ARGUMENT_CLASS_NAME))
+ .isEqualTo(RetryWorker::class.java.name)
+ }
+
@After
fun tearDown() {
workManager.cancelAllWork()
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt b/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
index 236e52c..b4435ea 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkerUpdater.kt
@@ -23,6 +23,7 @@
import androidx.work.WorkRequest
import androidx.work.impl.model.WorkSpec
import androidx.work.impl.utils.futures.SettableFuture
+import androidx.work.impl.utils.wrapInConstraintTrackingWorkerIfNeeded
import com.google.common.util.concurrent.ListenableFuture
private fun updateWorkImpl(
@@ -57,7 +58,8 @@
runAttemptCount = oldWorkSpec.runAttemptCount,
lastEnqueueTime = oldWorkSpec.lastEnqueueTime,
)
- workSpecDao.updateWorkSpec(updatedSpec)
+
+ workSpecDao.updateWorkSpec(wrapInConstraintTrackingWorkerIfNeeded(schedulers, updatedSpec))
workTagDao.deleteByWorkSpecId(workSpecId)
workTagDao.insertTags(workRequest)
if (!isEnqueued) {
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
index cbd798f..5408bb6 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
@@ -25,24 +25,20 @@
import static androidx.work.WorkInfo.State.FAILED;
import static androidx.work.WorkInfo.State.RUNNING;
import static androidx.work.WorkInfo.State.SUCCEEDED;
-import static androidx.work.impl.workers.ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME;
+import static androidx.work.impl.utils.EnqueueUtilsKt.wrapInConstraintTrackingWorkerIfNeeded;
import android.content.Context;
-import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
-import androidx.work.Constraints;
-import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.Logger;
import androidx.work.Operation;
import androidx.work.WorkInfo;
import androidx.work.WorkRequest;
import androidx.work.impl.OperationImpl;
-import androidx.work.impl.Scheduler;
import androidx.work.impl.Schedulers;
import androidx.work.impl.WorkContinuationImpl;
import androidx.work.impl.WorkDatabase;
@@ -53,7 +49,6 @@
import androidx.work.impl.model.WorkName;
import androidx.work.impl.model.WorkSpec;
import androidx.work.impl.model.WorkSpecDao;
-import androidx.work.impl.workers.ConstraintTrackingWorker;
import java.util.ArrayList;
import java.util.Collections;
@@ -306,20 +301,17 @@
workSpec.lastEnqueueTime = currentTimeMillis;
}
- if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
- && Build.VERSION.SDK_INT <= 25) {
- tryDelegateConstrainedWorkSpec(workSpec);
- } else if (Build.VERSION.SDK_INT <= WorkManagerImpl.MAX_PRE_JOB_SCHEDULER_API_LEVEL
- && usesScheduler(workManagerImpl, Schedulers.GCM_SCHEDULER)) {
- tryDelegateConstrainedWorkSpec(workSpec);
- }
-
// If we have one WorkSpec with an enqueued state, then we need to schedule.
if (workSpec.state == ENQUEUED) {
needsScheduling = true;
}
- workDatabase.workSpecDao().insertWorkSpec(workSpec);
+ workDatabase.workSpecDao().insertWorkSpec(
+ wrapInConstraintTrackingWorkerIfNeeded(
+ workManagerImpl.getSchedulers(),
+ workSpec
+ )
+ );
if (hasPrerequisite) {
for (String prerequisiteId : prerequisiteIds) {
@@ -335,50 +327,4 @@
}
return needsScheduling;
}
-
- private static void tryDelegateConstrainedWorkSpec(WorkSpec workSpec) {
- // requiresBatteryNotLow and requiresStorageNotLow require API 26 for JobScheduler.
- // Delegate to ConstraintTrackingWorker between API 23-25.
- Constraints constraints = workSpec.constraints;
- String workerClassName = workSpec.workerClassName;
- // Check if the Worker is a ConstraintTrackingWorker already. Otherwise we could end up
- // wrapping a ConstraintTrackingWorker with another and build a taller stack.
- // This usually happens when a developer accidentally enqueues() a named WorkRequest
- // with an ExistingWorkPolicy.KEEP and subsequent inserts no-op (while the state of the
- // Worker is not ENQUEUED or RUNNING i.e. the Worker probably just got done & the app is
- // holding on to a reference of WorkSpec which got updated). We end up reusing the
- // WorkSpec, and get a ConstraintTrackingWorker (instead of the original Worker class).
- boolean isConstraintTrackingWorker =
- workerClassName.equals(ConstraintTrackingWorker.class.getName());
- if (!isConstraintTrackingWorker
- && (constraints.requiresBatteryNotLow() || constraints.requiresStorageNotLow())) {
- Data.Builder builder = new Data.Builder();
- // Copy all arguments
- builder.putAll(workSpec.input)
- .putString(ARGUMENT_CLASS_NAME, workerClassName);
- workSpec.workerClassName = ConstraintTrackingWorker.class.getName();
- workSpec.input = builder.build();
- }
- }
-
- /**
- * @param className The fully qualified class name of the {@link Scheduler}
- * @return {@code true} if the {@link Scheduler} class is being used by WorkManager.
- */
- private static boolean usesScheduler(
- @NonNull WorkManagerImpl workManager,
- @NonNull String className) {
-
- try {
- Class> klass = Class.forName(className);
- for (Scheduler scheduler : workManager.getSchedulers()) {
- if (klass.isAssignableFrom(scheduler.getClass())) {
- return true;
- }
- }
- return false;
- } catch (ClassNotFoundException ignore) {
- return false;
- }
- }
}
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
new file mode 100644
index 0000000..a6cc806
--- /dev/null
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueUtils.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work.impl.utils
+
+import android.os.Build
+import androidx.work.Data
+import androidx.work.impl.Scheduler
+import androidx.work.impl.Schedulers
+import androidx.work.impl.WorkManagerImpl.MAX_PRE_JOB_SCHEDULER_API_LEVEL
+import androidx.work.impl.WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
+import androidx.work.impl.model.WorkSpec
+import androidx.work.impl.workers.ARGUMENT_CLASS_NAME
+import androidx.work.impl.workers.ConstraintTrackingWorker
+
+internal fun tryDelegateConstrainedWorkSpec(workSpec: WorkSpec): WorkSpec {
+ // requiresBatteryNotLow and requiresStorageNotLow require API 26 for JobScheduler.
+ // Delegate to ConstraintTrackingWorker between API 23-25.
+ val constraints = workSpec.constraints
+ val workerClassName = workSpec.workerClassName
+ // Check if the Worker is a ConstraintTrackingWorker already. Otherwise we could end up
+ // wrapping a ConstraintTrackingWorker with another and build a taller stack.
+ // This usually happens when a developer accidentally enqueues() a named WorkRequest
+ // with an ExistingWorkPolicy.KEEP and subsequent inserts no-op (while the state of the
+ // Worker is not ENQUEUED or RUNNING i.e. the Worker probably just got done & the app is
+ // holding on to a reference of WorkSpec which got updated). We end up reusing the
+ // WorkSpec, and get a ConstraintTrackingWorker (instead of the original Worker class).
+ val isConstraintTrackingWorker = workerClassName == ConstraintTrackingWorker::class.java.name
+ if (!isConstraintTrackingWorker &&
+ (constraints.requiresBatteryNotLow() || constraints.requiresStorageNotLow())
+ ) {
+ val newInputData = Data.Builder().putAll(workSpec.input)
+ .putString(ARGUMENT_CLASS_NAME, workerClassName)
+ .build()
+ return workSpec.copy(
+ input = newInputData,
+ workerClassName = ConstraintTrackingWorker::class.java.name
+ )
+ }
+ return workSpec
+}
+
+internal fun wrapInConstraintTrackingWorkerIfNeeded(
+ schedulers: List,
+ workSpec: WorkSpec,
+): WorkSpec {
+ return when {
+ Build.VERSION.SDK_INT in MIN_JOB_SCHEDULER_API_LEVEL..25 ->
+ tryDelegateConstrainedWorkSpec(workSpec)
+ Build.VERSION.SDK_INT <= MAX_PRE_JOB_SCHEDULER_API_LEVEL &&
+ usesScheduler(schedulers, Schedulers.GCM_SCHEDULER) ->
+ tryDelegateConstrainedWorkSpec(workSpec)
+ else -> workSpec
+ }
+}
+
+private fun usesScheduler(
+ schedulers: List,
+ className: String
+): Boolean {
+ return try {
+ val klass = Class.forName(className)
+ return schedulers.any { scheduler -> klass.isAssignableFrom(scheduler.javaClass) }
+ } catch (ignore: ClassNotFoundException) {
+ false
+ }
+}