Merge "feat: add left/right animation and minor prop renaming in Carousel" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index 0161db3..5ba8cb5 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -91,6 +91,7 @@
androidTestImplementation(project(":annotation:annotation-experimental"))
androidTestImplementation(project(":camera:camera-lifecycle"))
androidTestImplementation(project(":camera:camera-testing"))
+ androidTestImplementation(project(":camera:camera-video"))
androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
androidTestImplementation(project(":internal-testutils-truth"))
androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0")
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
index e5bccd9..a9e2aeb 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/CameraControlAdapterDeviceTest.kt
@@ -17,16 +17,25 @@
package androidx.camera.camera2.pipe.integration
import android.content.Context
+import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES
+import android.hardware.camera2.CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES
import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AE
import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AF
import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AWB
+import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF
import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON
import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH
import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH
+import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_AUTO
+import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO
+import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_OFF
import android.hardware.camera2.CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION
import android.hardware.camera2.CaptureRequest.CONTROL_AE_MODE
import android.hardware.camera2.CaptureRequest.CONTROL_AE_REGIONS
+import android.hardware.camera2.CaptureRequest.CONTROL_AF_MODE
import android.hardware.camera2.CaptureRequest.CONTROL_AF_REGIONS
import android.hardware.camera2.CaptureRequest.CONTROL_AWB_REGIONS
import android.hardware.camera2.CaptureRequest.CONTROL_CAPTURE_INTENT
@@ -36,6 +45,7 @@
import android.hardware.camera2.CaptureRequest.FLASH_MODE_TORCH
import android.hardware.camera2.CaptureRequest.SCALER_CROP_REGION
import android.os.Build
+import android.util.Size
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.FrameInfo
import androidx.camera.camera2.pipe.RequestMetadata
@@ -49,11 +59,15 @@
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
+import androidx.camera.core.Preview
import androidx.camera.core.SurfaceOrientedMeteringPointFactory
import androidx.camera.core.UseCase
import androidx.camera.core.internal.CameraUseCaseAdapter
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
+import androidx.camera.testing.SurfaceTextureProvider
+import androidx.camera.video.Recorder
+import androidx.camera.video.VideoCapture
import androidx.concurrent.futures.await
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -188,7 +202,7 @@
waitForResult(captureCount = 60).verify(
{ requestMeta: RequestMetadata, _ ->
- requestMeta.request[CONTROL_AE_MODE] == CONTROL_AE_MODE_ON_AUTO_FLASH
+ requestMeta.isAeMode(CONTROL_AE_MODE_ON_AUTO_FLASH)
},
TIMEOUT
)
@@ -203,7 +217,7 @@
waitForResult(captureCount = 60).verify(
{ requestMeta: RequestMetadata, _ ->
- requestMeta[CONTROL_AE_MODE] == CONTROL_AE_MODE_ON
+ requestMeta.isAeMode(CONTROL_AE_MODE_ON)
},
TIMEOUT
)
@@ -218,7 +232,7 @@
waitForResult(captureCount = 60).verify(
{ requestMeta: RequestMetadata, _ ->
- requestMeta[CONTROL_AE_MODE] == CONTROL_AE_MODE_ON_ALWAYS_FLASH
+ requestMeta.isAeMode(CONTROL_AE_MODE_ON_ALWAYS_FLASH)
},
TIMEOUT
)
@@ -234,7 +248,7 @@
waitForResult(captureCount = 30).verify(
{ requestMeta: RequestMetadata, frameInfo: FrameInfo ->
frameInfo.requestMetadata[FLASH_MODE] == FLASH_MODE_TORCH &&
- requestMeta[CONTROL_AE_MODE] == CONTROL_AE_MODE_ON
+ requestMeta.isAeMode(CONTROL_AE_MODE_ON)
},
TIMEOUT
)
@@ -250,7 +264,7 @@
waitForResult(captureCount = 30).verify(
{ requestMeta: RequestMetadata, frameInfo: FrameInfo ->
frameInfo.requestMetadata[FLASH_MODE] != FLASH_MODE_TORCH &&
- requestMeta[CONTROL_AE_MODE] == CONTROL_AE_MODE_ON_AUTO_FLASH
+ requestMeta.isAeMode(CONTROL_AE_MODE_ON_AUTO_FLASH)
},
TIMEOUT
)
@@ -341,6 +355,32 @@
}
@Test
+ fun setTemplatePreview_afModeToContinuousPicture() = runBlocking {
+ bindUseCase(createPreview())
+
+ // Assert. Verify the afMode.
+ waitForResult(captureCount = 60).verify(
+ { requestMeta: RequestMetadata, _ ->
+ requestMeta.isAfMode(CONTROL_AF_MODE_CONTINUOUS_PICTURE)
+ },
+ TIMEOUT
+ )
+ }
+
+ @Test
+ fun setTemplateRecord_afModeToContinuousVideo() = runBlocking {
+ bindUseCase(createVideoCapture())
+
+ // Assert. Verify the afMode.
+ waitForResult(captureCount = 60).verify(
+ { requestMeta: RequestMetadata, _ ->
+ requestMeta.isAfMode(CONTROL_AF_MODE_CONTINUOUS_VIDEO)
+ },
+ TIMEOUT
+ )
+ }
+
+ @Test
fun setZoomRatio_operationCanceledExceptionIfNoUseCase() {
assertFutureFailedWithOperationCancellation(cameraControl.setZoomRatio(1.5f))
}
@@ -436,4 +476,70 @@
)
cameraControl = camera.cameraControl as CameraControlAdapter
}
+
+ private fun createVideoCapture(): VideoCapture<Recorder> {
+ return VideoCapture.withOutput(Recorder.Builder().build())
+ }
+
+ private suspend fun createPreview(): Preview =
+ Preview.Builder().build().also { preview ->
+ withContext(Dispatchers.Main) {
+ preview.setSurfaceProvider(getSurfaceProvider())
+ }
+ }
+
+ private fun getSurfaceProvider(): Preview.SurfaceProvider {
+ return SurfaceTextureProvider.createSurfaceTextureProvider(
+ object : SurfaceTextureProvider.SurfaceTextureCallback {
+ override fun onSurfaceTextureReady(
+ surfaceTexture: SurfaceTexture,
+ resolution: Size
+ ) {
+ // No-op
+ }
+
+ override fun onSafeToRelease(surfaceTexture: SurfaceTexture) {
+ surfaceTexture.release()
+ }
+ }
+ )
+ }
+
+ private fun RequestMetadata.isAfMode(afMode: Int): Boolean {
+ return if (characteristics.isAfModeSupported(afMode)) {
+ getOrDefault(CONTROL_AF_MODE, null) == afMode
+ } else {
+ val fallbackMode =
+ if (characteristics.isAfModeSupported(CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
+ CONTROL_AF_MODE_CONTINUOUS_PICTURE
+ } else if (characteristics.isAfModeSupported(CONTROL_AF_MODE_AUTO)) {
+ CONTROL_AF_MODE_AUTO
+ } else {
+ CONTROL_AF_MODE_OFF
+ }
+ getOrDefault(CONTROL_AF_MODE, null) == fallbackMode
+ }
+ }
+
+ private fun RequestMetadata.isAeMode(aeMode: Int): Boolean {
+ return if (characteristics.isAeModeSupported(aeMode)) {
+ getOrDefault(CONTROL_AE_MODE, null) == aeMode
+ } else {
+ val fallbackMode =
+ if (characteristics.isAeModeSupported(CONTROL_AE_MODE_ON)) {
+ CONTROL_AE_MODE_ON
+ } else {
+ CONTROL_AE_MODE_OFF
+ }
+ getOrDefault(CONTROL_AE_MODE, null) == fallbackMode
+ }
+ }
+
+ private fun CameraCharacteristics.isAfModeSupported(
+ afMode: Int
+ ) = (get(CONTROL_AF_AVAILABLE_MODES) ?: intArrayOf(-1)).contains(afMode)
+
+ private fun CameraCharacteristics.isAeModeSupported(
+ aeMode: Int
+ ) = (get(CONTROL_AE_AVAILABLE_MODES) ?: intArrayOf(-1)).contains(aeMode)
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
index 4c1ef19..de3e23f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
@@ -35,6 +35,7 @@
import androidx.camera.camera2.pipe.integration.impl.EvCompControl
import androidx.camera.camera2.pipe.integration.impl.FlashControl
import androidx.camera.camera2.pipe.integration.impl.FocusMeteringControl
+import androidx.camera.camera2.pipe.integration.impl.State3AControl
import androidx.camera.camera2.pipe.integration.impl.TorchControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
import androidx.camera.camera2.pipe.integration.impl.ZoomControl
@@ -67,6 +68,7 @@
EvCompControl.Bindings::class,
FlashControl.Bindings::class,
FocusMeteringControl.Bindings::class,
+ State3AControl.Bindings::class,
TorchControl.Bindings::class,
Camera2CameraControlCompat.Bindings::class,
],
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
index 0a00d07..21fe187 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
@@ -16,7 +16,6 @@
package androidx.camera.camera2.pipe.integration.impl
-import android.hardware.camera2.CaptureRequest
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.integration.config.CameraScope
import androidx.camera.core.CameraControl
@@ -25,12 +24,12 @@
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
+import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.launch
-import javax.inject.Inject
-private const val DEFAULT_FLASH_MODE = ImageCapture.FLASH_MODE_OFF
+internal const val DEFAULT_FLASH_MODE = ImageCapture.FLASH_MODE_OFF
/**
* Implementation of Flash control exposed by [CameraControlInternal].
@@ -38,6 +37,7 @@
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@CameraScope
class FlashControl @Inject constructor(
+ private val state3AControl: State3AControl,
private val threads: UseCaseThreads,
) : UseCaseCameraControl {
private var _useCaseCamera: UseCaseCamera? = null
@@ -74,7 +74,7 @@
fun setFlashAsync(flashMode: Int): Deferred<Unit> {
val signal = CompletableDeferred<Unit>()
- useCaseCamera?.let { useCaseCamera ->
+ useCaseCamera?.let {
// Update _flashMode immediately so that CameraControlInternal#getFlashMode()
// returns correct value.
@@ -84,24 +84,8 @@
stopRunningTask()
_updateSignal = signal
- when (flashMode) {
- ImageCapture.FLASH_MODE_OFF -> CaptureRequest.CONTROL_AE_MODE_ON
- ImageCapture.FLASH_MODE_ON -> CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH
- ImageCapture.FLASH_MODE_AUTO -> CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
- // TODO(b/209383160): porting the Quirk for AEModeDisabler
- // mAutoFlashAEModeDisabler.getCorrectedAeMode(
- // CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
- // )
- else -> CaptureRequest.CONTROL_AE_MODE_ON
- }.let { aeMode ->
- // TODO: check the AE mode is supported before set it.
- useCaseCamera.requestControl.addParametersAsync(
- type = UseCaseCameraRequestControl.Type.FLASH,
- values = mapOf(
- CaptureRequest.CONTROL_AE_MODE to aeMode,
- )
- )
- }.join()
+ state3AControl.flashMode = flashMode
+ state3AControl.updateSignal?.join()
signal.complete(Unit)
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
index 43b6750..08fd80a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
@@ -19,6 +19,7 @@
import android.graphics.PointF
import android.graphics.Rect
import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.params.MeteringRectangle
import android.util.Rational
@@ -54,8 +55,9 @@
@CameraScope
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class FocusMeteringControl @Inject constructor(
- val cameraProperties: CameraProperties,
- val threads: UseCaseThreads,
+ private val cameraProperties: CameraProperties,
+ private val state3AControl: State3AControl,
+ private val threads: UseCaseThreads,
) : UseCaseCameraControl {
private var _useCaseCamera: UseCaseCamera? = null
@@ -155,6 +157,9 @@
)
return@launch
}
+ if (afRectangles.isNotEmpty()) {
+ state3AControl.preferredFocusMode = CaptureRequest.CONTROL_AF_MODE_AUTO
+ }
val (isCancelEnabled, timeout) = if (action.isAutoCancelEnabled &&
action.autoCancelDurationInMillis < autoFocusTimeoutMs
) {
@@ -259,6 +264,7 @@
signalToCancel: CompletableDeferred<FocusMeteringResult>?,
): Result3A {
signalToCancel?.setCancelException("Cancelled by cancelFocusAndMetering()")
+ state3AControl.preferredFocusMode = null
return useCaseCamera.requestControl.cancelFocusAndMeteringAsync().await()
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt
new file mode 100644
index 0000000..740438c
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CaptureRequest
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
+import androidx.camera.camera2.pipe.integration.config.CameraScope
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.UseCase
+import androidx.camera.core.impl.CaptureConfig
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.lifecycle.Observer
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlin.properties.ObservableProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+
+@CameraScope
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class State3AControl @Inject constructor(
+ val cameraProperties: CameraProperties,
+) : UseCaseCameraControl {
+ private var _useCaseCamera: UseCaseCamera? = null
+ override var useCaseCamera: UseCaseCamera?
+ get() = _useCaseCamera
+ set(value) {
+ val previousUseCaseCamera = _useCaseCamera
+ _useCaseCamera = value
+ CameraXExecutors.mainThreadExecutor().execute {
+ previousUseCaseCamera?.runningUseCasesLiveData?.removeObserver(
+ useCaseChangeObserver
+ )
+ value?.let {
+ it.runningUseCasesLiveData.observeForever(useCaseChangeObserver)
+ invalidate() // Always apply the settings to the camera.
+ }
+ }
+ }
+
+ private val useCaseChangeObserver =
+ Observer<Set<UseCase>> { useCases -> useCases.updateTemplate() }
+ private val afModes = cameraProperties.metadata.getOrDefault(
+ CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES,
+ intArrayOf(CaptureRequest.CONTROL_AF_MODE_OFF)
+ ).asList()
+ private val aeModes = cameraProperties.metadata.getOrDefault(
+ CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES,
+ intArrayOf(CaptureRequest.CONTROL_AE_MODE_OFF)
+ ).asList()
+ private val awbModes = cameraProperties.metadata.getOrDefault(
+ CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES,
+ intArrayOf(CaptureRequest.CONTROL_AWB_MODE_OFF)
+ ).asList()
+
+ var updateSignal: Deferred<Unit>? = null
+ private set
+ var flashMode by updateOnPropertyChange(DEFAULT_FLASH_MODE)
+ var template by updateOnPropertyChange(DEFAULT_REQUEST_TEMPLATE)
+ var preferredAeMode: Int? by updateOnPropertyChange(null)
+ var preferredFocusMode: Int? by updateOnPropertyChange(null)
+
+ override fun reset() {
+ preferredAeMode = null
+ preferredFocusMode = null
+ flashMode = DEFAULT_FLASH_MODE
+ template = DEFAULT_REQUEST_TEMPLATE
+ }
+
+ private fun <T> updateOnPropertyChange(
+ initialValue: T
+ ) = object : ObservableProperty<T>(initialValue) {
+ override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
+ if (newValue != oldValue) {
+ invalidate()
+ }
+ }
+ }
+
+ fun invalidate() {
+ val preferAeMode = preferredAeMode ?: when (flashMode) {
+ ImageCapture.FLASH_MODE_OFF -> CaptureRequest.CONTROL_AE_MODE_ON
+ ImageCapture.FLASH_MODE_ON -> CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH
+ ImageCapture.FLASH_MODE_AUTO -> CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
+ // TODO(b/209383160): porting the Quirk for AEModeDisabler
+ // mAutoFlashAEModeDisabler.getCorrectedAeMode(
+ // CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH
+ // )
+ else -> CaptureRequest.CONTROL_AE_MODE_ON
+ }
+
+ val preferAfMode = preferredFocusMode ?: getDefaultAfMode()
+
+ updateSignal = useCaseCamera?.requestControl?.addParametersAsync(
+ values = mapOf(
+ CaptureRequest.CONTROL_AE_MODE to getSupportedAeMode(preferAeMode),
+ CaptureRequest.CONTROL_AF_MODE to getSupportedAfMode(preferAfMode),
+ CaptureRequest.CONTROL_AWB_MODE to getSupportedAwbMode(
+ CaptureRequest.CONTROL_AWB_MODE_AUTO
+ ),
+ )
+ ) ?: CompletableDeferred(null)
+ }
+
+ private fun getDefaultAfMode(): Int = when (template) {
+ CameraDevice.TEMPLATE_RECORD -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO
+ CameraDevice.TEMPLATE_PREVIEW -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+ else -> CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+ }
+
+ /**
+ * If preferredMode not available, priority is CONTINUOUS_PICTURE > AUTO > OFF
+ */
+ private fun getSupportedAfMode(preferredMode: Int) = when {
+ afModes.contains(preferredMode) -> {
+ preferredMode
+ }
+
+ afModes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) -> {
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
+ }
+
+ afModes.contains(CaptureRequest.CONTROL_AF_MODE_AUTO) -> {
+ CaptureRequest.CONTROL_AF_MODE_AUTO
+ }
+
+ else -> {
+ CaptureRequest.CONTROL_AF_MODE_OFF
+ }
+ }
+
+ /**
+ * If preferredMode not available, priority is AE_ON > AE_OFF
+ */
+ private fun getSupportedAeMode(preferredMode: Int) = when {
+ aeModes.contains(preferredMode) -> {
+ preferredMode
+ }
+
+ aeModes.contains(CaptureRequest.CONTROL_AE_MODE_ON) -> {
+ CaptureRequest.CONTROL_AE_MODE_ON
+ }
+
+ else -> {
+ CaptureRequest.CONTROL_AE_MODE_OFF
+ }
+ }
+
+ /**
+ * If preferredMode not available, priority is AWB_AUTO > AWB_OFF
+ */
+ private fun getSupportedAwbMode(preferredMode: Int) = when {
+ awbModes.contains(preferredMode) -> {
+ preferredMode
+ }
+
+ awbModes.contains(CaptureRequest.CONTROL_AWB_MODE_AUTO) -> {
+ CaptureRequest.CONTROL_AWB_MODE_AUTO
+ }
+
+ else -> {
+ CaptureRequest.CONTROL_AWB_MODE_OFF
+ }
+ }
+
+ private fun Collection<UseCase>.updateTemplate() {
+ SessionConfigAdapter(this).getValidSessionConfigOrNull()?.let {
+ val templateType = it.repeatingCaptureConfig.templateType
+ template = if (templateType != CaptureConfig.TEMPLATE_TYPE_NONE) {
+ templateType
+ } else {
+ DEFAULT_REQUEST_TEMPLATE
+ }
+ }
+ }
+
+ @Module
+ abstract class Bindings {
+ @Binds
+ @IntoSet
+ abstract fun provideControls(state3AControl: State3AControl): UseCaseCameraControl
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
index ffce1ecb..7981dc9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
@@ -29,10 +29,10 @@
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
+import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Implementation of Torch control exposed by [CameraControlInternal].
@@ -41,6 +41,7 @@
@CameraScope
class TorchControl @Inject constructor(
cameraProperties: CameraProperties,
+ private val state3AControl: State3AControl,
private val threads: UseCaseThreads,
) : UseCaseCameraControl {
@@ -94,20 +95,10 @@
// TODO(b/209757083), handle the failed result of the setTorchAsync().
useCaseCamera.requestControl.setTorchAsync(torch).join()
- if (torch) {
- // Hold the internal AE mode to ON while the torch is turned ON.
- useCaseCamera.requestControl.addParametersAsync(
- type = UseCaseCameraRequestControl.Type.TORCH,
- values = mapOf(
- CaptureRequest.CONTROL_AE_MODE to CaptureRequest.CONTROL_AE_MODE_ON,
- )
- )
- } else {
- // Restore the AE mode after the torch control has been used.
- useCaseCamera.requestControl.setConfigAsync(
- type = UseCaseCameraRequestControl.Type.TORCH,
- )
- }.join()
+ // Hold the internal AE mode to ON while the torch is turned ON.
+ state3AControl.preferredAeMode =
+ if (torch) CaptureRequest.CONTROL_AE_MODE_ON else null
+ state3AControl.updateSignal?.join()
signal.complete(Unit)
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index 3d9377d..852b4f4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -48,7 +48,7 @@
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
-private const val DEFAULT_REQUEST_TEMPLATE = CameraDevice.TEMPLATE_PREVIEW
+internal const val DEFAULT_REQUEST_TEMPLATE = CameraDevice.TEMPLATE_PREVIEW
/**
* The RequestControl provides a couple of APIs to update the config of the camera, it also stores
@@ -66,8 +66,6 @@
enum class Type {
SESSION_CONFIG,
DEFAULT,
- FLASH,
- TORCH,
CAMERA2_CAMERA_CONTROL,
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
index 8ca5d39..5c5af8b 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
@@ -31,6 +31,7 @@
import androidx.camera.camera2.pipe.Result3A
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
import androidx.camera.camera2.pipe.integration.impl.FocusMeteringControl
+import androidx.camera.camera2.pipe.integration.impl.State3AControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
@@ -1028,19 +1029,84 @@
assertFutureFocusCompleted(future, false)
}
+ @Test
+ fun startFocusMetering_afAutoModeIsSet() {
+ // Arrange.
+ val action = FocusMeteringAction.Builder(point1, FocusMeteringAction.FLAG_AF).build()
+ val state3AControl = createState3AControl(CAMERA_ID_0)
+ focusMeteringControl = initFocusMeteringControl(
+ CAMERA_ID_0,
+ setOf(createPreview(Size(1920, 1080))),
+ fakeUseCaseThreads,
+ state3AControl,
+ )
+
+ // Act.
+ focusMeteringControl.startFocusAndMetering(
+ action
+ )[5, TimeUnit.SECONDS]
+
+ // Assert.
+ assertThat(
+ state3AControl.preferredFocusMode
+ ).isEqualTo(CaptureRequest.CONTROL_AF_MODE_AUTO)
+ }
+
+ @Test
+ fun startFocusMetering_AfNotInvolved_afAutoModeNotSet() {
+ // Arrange.
+ val action = FocusMeteringAction.Builder(
+ point1,
+ FocusMeteringAction.FLAG_AE or FocusMeteringAction.FLAG_AWB
+ ).build()
+ val state3AControl = createState3AControl(CAMERA_ID_0)
+ focusMeteringControl = initFocusMeteringControl(
+ CAMERA_ID_0,
+ setOf(createPreview(Size(1920, 1080))),
+ fakeUseCaseThreads,
+ state3AControl,
+ )
+
+ // Act.
+ focusMeteringControl.startFocusAndMetering(
+ action
+ )[5, TimeUnit.SECONDS]
+
+ // Assert.
+ assertThat(
+ state3AControl.preferredFocusMode
+ ).isEqualTo(null)
+ }
+
+ @Test
+ fun startAndThenCancel_afAutoModeNotSet(): Unit = runBlocking {
+ // Arrange.
+ val action = FocusMeteringAction.Builder(point1, FocusMeteringAction.FLAG_AF).build()
+ val state3AControl = createState3AControl(CAMERA_ID_0)
+ focusMeteringControl = initFocusMeteringControl(
+ CAMERA_ID_0,
+ setOf(createPreview(Size(1920, 1080))),
+ fakeUseCaseThreads,
+ state3AControl,
+ )
+
+ // Act.
+ focusMeteringControl.startFocusAndMetering(
+ action
+ )[5, TimeUnit.SECONDS]
+ focusMeteringControl.cancelFocusAndMeteringAsync().join()
+
+ // Assert.
+ assertThat(
+ state3AControl.preferredFocusMode
+ ).isEqualTo(null)
+ }
+
// TODO: Port the following tests once their corresponding logics have been implemented.
// - [b/255679866] triggerAfWithTemplate, triggerAePrecaptureWithTemplate,
// cancelAfAeTriggerWithTemplate
// - startFocusAndMetering_AfRegionCorrectedByQuirk
// - [b/262225455] cropRegionIsSet_resultBasedOnCropRegion
- // The following ones will depend on how exactly they will be implemented.
- // - [b/264018162] addFocusMeteringOptions_hasCorrectAfMode,
- // startFocusMetering_isAfAutoModeIsTrue,
- // startFocusMetering_AfNotInvolved_isAfAutoModeIsSet,
- // startAndThenCancel_isAfAutoModeIsFalse
- // (an alternative way can be checking the AF mode
- // at the frame with AF_TRIGGER_START request in capture callback, but this requires
- // invoking actual camera operations, ref: TapToFocusDeviceTest)
private fun assertFutureFocusCompleted(
future: ListenableFuture<FocusMeteringResult>,
@@ -1135,8 +1201,11 @@
cameraId: String,
useCases: Set<UseCase> = emptySet(),
useCaseThreads: UseCaseThreads = fakeUseCaseThreads,
+ state3AControl: State3AControl = createState3AControl(cameraId),
) = FocusMeteringControl(
- cameraPropertiesMap[cameraId]!!, useCaseThreads
+ cameraPropertiesMap[cameraId]!!,
+ state3AControl,
+ useCaseThreads
).apply {
fakeUseCaseCamera.runningUseCasesLiveData.value = useCases
useCaseCamera = fakeUseCaseCamera
@@ -1260,4 +1329,12 @@
StreamSpec.builder(suggestedStreamSpecResolution).build()
)
}
+
+ private fun createState3AControl(
+ cameraId: String = CAMERA_ID_0,
+ properties: CameraProperties = cameraPropertiesMap[cameraId]!!,
+ useCaseCamera: UseCaseCamera = fakeUseCaseCamera,
+ ) = State3AControl(properties).apply {
+ this.useCaseCamera = useCaseCamera
+ }
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index 6337c98..f6a563e 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -177,15 +177,21 @@
@Before
fun setUp() {
+ val fakeUseCaseCamera = FakeUseCaseCamera(requestControl = fakeRequestControl)
+ val fakeCameraProperties = FakeCameraProperties(
+ FakeCameraMetadata(
+ mapOf(CameraCharacteristics.FLASH_INFO_AVAILABLE to true),
+ )
+ )
+
torchControl = TorchControl(
- FakeCameraProperties(
- FakeCameraMetadata(
- mapOf(CameraCharacteristics.FLASH_INFO_AVAILABLE to true),
- )
- ),
+ fakeCameraProperties,
+ State3AControl(fakeCameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
+ },
fakeUseCaseThreads,
).also {
- it.useCaseCamera = FakeUseCaseCamera(requestControl = fakeRequestControl)
+ it.useCaseCamera = fakeUseCaseCamera
// Ensure the control is updated after the UseCaseCamera been set.
assertThat(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/TorchControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/TorchControlTest.kt
index be46710..6b24bc2 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/TorchControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/TorchControlTest.kt
@@ -147,33 +147,50 @@
@Before
fun setUp() {
+ val fakeUseCaseCamera = FakeUseCaseCamera()
+ val fakeCameraProperties = FakeCameraProperties(metadata)
torchControl = TorchControl(
- FakeCameraProperties(metadata),
+ fakeCameraProperties,
+ State3AControl(fakeCameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
+ },
fakeUseCaseThreads,
)
- torchControl.useCaseCamera = FakeUseCaseCamera()
+ torchControl.useCaseCamera = fakeUseCaseCamera
}
@Test
fun enableTorch_whenNoFlashUnit(): Unit = runBlocking {
assertThrows<IllegalStateException> {
+ val fakeUseCaseCamera = FakeUseCaseCamera()
+ val fakeCameraProperties = FakeCameraProperties()
+
// Without a flash unit, this Job will complete immediately with a IllegalStateException
TorchControl(
- FakeCameraProperties(),
+ fakeCameraProperties,
+ State3AControl(fakeCameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
+ },
fakeUseCaseThreads,
).also {
- it.useCaseCamera = FakeUseCaseCamera()
+ it.useCaseCamera = fakeUseCaseCamera
}.setTorchAsync(true).await()
}
}
@Test
fun getTorchState_whenNoFlashUnit() {
+ val fakeUseCaseCamera = FakeUseCaseCamera()
+ val fakeCameraProperties = FakeCameraProperties()
+
val torchState = TorchControl(
- FakeCameraProperties(),
+ fakeCameraProperties,
+ State3AControl(fakeCameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
+ },
fakeUseCaseThreads,
).also {
- it.useCaseCamera = FakeUseCaseCamera()
+ it.useCaseCamera = fakeUseCaseCamera
}.torchStateLiveData.value
Truth.assertThat(torchState).isEqualTo(TorchState.OFF)
@@ -182,8 +199,14 @@
@Test
fun enableTorch_whenInactive(): Unit = runBlocking {
assertThrows<CameraControl.OperationCanceledException> {
+ val fakeUseCaseCamera = FakeUseCaseCamera()
+ val fakeCameraProperties = FakeCameraProperties(metadata)
+
TorchControl(
- FakeCameraProperties(metadata),
+ fakeCameraProperties,
+ State3AControl(fakeCameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
+ },
fakeUseCaseThreads,
).setTorchAsync(true).await()
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
index 5c6e224..e98ffc8 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
@@ -29,6 +29,7 @@
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
import androidx.camera.camera2.pipe.integration.impl.EvCompControl
import androidx.camera.camera2.pipe.integration.impl.FocusMeteringControl
+import androidx.camera.camera2.pipe.integration.impl.State3AControl
import androidx.camera.camera2.pipe.integration.impl.TorchControl
import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
import androidx.camera.camera2.pipe.integration.impl.ZoomControl
@@ -79,21 +80,28 @@
cameraId
),
zoomControl: ZoomControl = this.zoomControl,
- ) = CameraInfoAdapter(
- cameraProperties,
- CameraConfig(cameraId),
- CameraStateAdapter(),
- CameraControlStateAdapter(
- zoomControl,
- EvCompControl(FakeEvCompCompat()),
- TorchControl(cameraProperties, useCaseThreads),
- ),
- CameraCallbackMap(),
- FocusMeteringControl(
- cameraProperties,
- useCaseThreads
- ).apply {
- useCaseCamera = FakeUseCaseCamera()
+ ): CameraInfoAdapter {
+ val fakeUseCaseCamera = FakeUseCaseCamera()
+ val state3AControl = State3AControl(cameraProperties).apply {
+ useCaseCamera = fakeUseCaseCamera
}
- )
+ return CameraInfoAdapter(
+ cameraProperties,
+ CameraConfig(cameraId),
+ CameraStateAdapter(),
+ CameraControlStateAdapter(
+ zoomControl,
+ EvCompControl(FakeEvCompCompat()),
+ TorchControl(cameraProperties, state3AControl, useCaseThreads),
+ ),
+ CameraCallbackMap(),
+ FocusMeteringControl(
+ cameraProperties,
+ state3AControl,
+ useCaseThreads
+ ).apply {
+ useCaseCamera = fakeUseCaseCamera
+ }
+ )
+ }
}
diff --git a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathMeasureTest.kt b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathMeasureTest.kt
index afacfe3..64a36aa 100644
--- a/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathMeasureTest.kt
+++ b/compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/PathMeasureTest.kt
@@ -41,9 +41,9 @@
val tangent = pathMeasure.getTangent(distance * 0.5f)
- Assert.assertEquals(50f, position.x)
- Assert.assertEquals(50f, position.y)
- Assert.assertEquals(0.707106f, tangent.x, 0.00001f)
- Assert.assertEquals(0.707106f, tangent.y, 0.00001f)
+ Assert.assertEquals(50f, position.x, 0.0001f)
+ Assert.assertEquals(50f, position.y, 0.0001f)
+ Assert.assertEquals(0.707106f, tangent.x, 0.0001f)
+ Assert.assertEquals(0.707106f, tangent.y, 0.0001f)
}
}
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
index bc848ab..4742770 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
@@ -32,13 +32,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString;
import androidx.wear.watchface.complications.data.ComplicationData;
import androidx.wear.watchface.complications.data.ComplicationText;
import androidx.wear.watchface.complications.data.ComplicationType;
import androidx.wear.watchface.complications.data.LongTextComplicationData;
import androidx.wear.watchface.complications.data.PlainComplicationText;
-import androidx.wear.watchface.complications.data.StringExpression;
-import androidx.wear.watchface.complications.data.StringExpressionComplicationText;
+import androidx.wear.watchface.complications.data.ComplicationTextExpression;
import org.junit.After;
import org.junit.Before;
@@ -195,8 +196,9 @@
throws Exception {
mService.responseData =
new LongTextComplicationData.Builder(
- new StringExpressionComplicationText(
- new StringExpression(new byte[]{1, 2})),
+ new ComplicationTextExpression(
+ DynamicString.constant("hello").concat(
+ DynamicString.constant(" world"))),
ComplicationText.EMPTY)
.build();
@@ -209,8 +211,9 @@
verify(mRemoteManager).updateComplicationData(
eq(123),
eq(new LongTextComplicationData.Builder(
- new StringExpressionComplicationText(
- new StringExpression(new byte[]{1, 2})),
+ new ComplicationTextExpression(
+ DynamicString.constant("hello").concat(
+ DynamicString.constant(" world"))),
ComplicationText.EMPTY)
.build()
.asWireComplicationData()));
@@ -222,8 +225,9 @@
throws Exception {
mService.responseData =
new LongTextComplicationData.Builder(
- new StringExpressionComplicationText(
- new StringExpression(new byte[]{1, 2})),
+ new ComplicationTextExpression(
+ DynamicString.constant("hello").concat(
+ DynamicString.constant(" world"))),
ComplicationText.EMPTY)
.build();
@@ -236,9 +240,10 @@
verify(mRemoteManager).updateComplicationData(
eq(123),
eq(new LongTextComplicationData.Builder(
- // TODO(b/260065006): Verify that it is actually evaluated.
- new StringExpressionComplicationText(
- new StringExpression(new byte[]{1, 2})),
+ // TODO(b/260065006): new PlainComplicationText.Builder("hello world")
+ new ComplicationTextExpression(
+ DynamicString.constant("hello").concat(
+ DynamicString.constant(" world"))),
ComplicationText.EMPTY)
.build()
.asWireComplicationData()));
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
index 4dccf73..8ea484e 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
@@ -132,9 +132,9 @@
assertThat(TIMELINE_A.toString()).isEqualTo(
"ComplicationDataTimeline(defaultComplicationData=ShortTextComplicationData("
+ "text=ComplicationText{mSurroundingText=Hello, mTimeDependentText=null, "
- + "mStringExpression=null}, title=null, monochromaticImage=null, "
+ + "mExpression=null}, title=null, monochromaticImage=null, "
+ "smallImage=null, contentDescription=ComplicationText{"
- + "mSurroundingText=, mTimeDependentText=null, mStringExpression=null}, "
+ + "mSurroundingText=, mTimeDependentText=null, mExpression=null}, "
+ "tapActionLostDueToSerialization=false, tapAction=null, "
+ "validTimeRange=TimeRange(startDateTimeMillis="
+ "-1000000000-01-01T00:00:00Z, endDateTimeMillis="
@@ -143,10 +143,10 @@
+ "TimelineEntry(validity=TimeInterval(start=1970-01-02T03:46:40Z, "
+ "end=1970-01-03T07:33:20Z), complicationData=ShortTextComplicationData("
+ "text=ComplicationText{mSurroundingText=Updated, "
- + "mTimeDependentText=null, mStringExpression=null}, title=null, "
+ + "mTimeDependentText=null, mExpression=null}, title=null, "
+ "monochromaticImage=null, smallImage=null, "
+ "contentDescription=ComplicationText{mSurroundingText=, "
- + "mTimeDependentText=null, mStringExpression=null}, "
+ + "mTimeDependentText=null, mExpression=null}, "
+ "tapActionLostDueToSerialization=false, tapAction=null, "
+ "validTimeRange=TimeRange(startDateTimeMillis="
+ "-1000000000-01-01T00:00:00Z, endDateTimeMillis="
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index c657cfb..40675d8 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -94,9 +94,6 @@
field public static final androidx.wear.watchface.complications.data.ComplicationType TYPE;
}
- public final class FloatExpressionKt {
- }
-
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class GoalProgressComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
diff --git a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
index 0bb17ae..d49c322 100644
--- a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
@@ -97,9 +97,6 @@
field public static final androidx.wear.watchface.complications.data.ComplicationType TYPE;
}
- public final class FloatExpressionKt {
- }
-
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class GoalProgressComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index f0e643f..58155a5 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -94,9 +94,6 @@
field public static final androidx.wear.watchface.complications.data.ComplicationType TYPE;
}
- public final class FloatExpressionKt {
- }
-
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class GoalProgressComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
diff --git a/wear/watchface/watchface-complications-data/build.gradle b/wear/watchface/watchface-complications-data/build.gradle
index 953db5b..3b9d23c 100644
--- a/wear/watchface/watchface-complications-data/build.gradle
+++ b/wear/watchface/watchface-complications-data/build.gradle
@@ -30,6 +30,11 @@
api("androidx.versionedparcelable:versionedparcelable:1.1.0")
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesAndroid)
+
+ // TODO(b/267170061): Use released protolayout-expression, remove protolayout-proto.
+ api(project(":wear:protolayout:protolayout-expression"))
+ implementation(project(":wear:protolayout:protolayout-proto"))
+
implementation("androidx.core:core:1.1.0")
implementation("androidx.preference:preference:1.1.0")
implementation("androidx.annotation:annotation:1.2.0")
@@ -44,6 +49,7 @@
testImplementation(libs.junit)
testImplementation(libs.kotlinTest)
testImplementation(libs.testParameterInjector)
+ testImplementation(libs.guavaTestlib)
annotationProcessor(project(":versionedparcelable:versionedparcelable-compiler"))
}
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
index 41954c9..813758c 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
@@ -30,12 +30,11 @@
import androidx.annotation.IntDef
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy
import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy
-import androidx.wear.watchface.complications.data.FloatExpression
-import androidx.wear.watchface.complications.data.toFloatExpression
import androidx.wear.watchface.utility.iconEquals
import androidx.wear.watchface.utility.iconHashCode
import java.io.IOException
@@ -43,6 +42,7 @@
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
+import java.util.Arrays
import java.util.Objects
/**
@@ -179,7 +179,7 @@
}
if (isFieldValidForType(FIELD_VALUE_EXPRESSION, type)) {
oos.writeNullable(complicationData.rangedValueExpression) {
- oos.writeByteArray(it.asByteArray())
+ oos.writeByteArray(it.toDynamicFloatByteArray())
}
}
if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
@@ -607,10 +607,11 @@
* Valid only if the type of this complication data is [TYPE_RANGED_VALUE] and
* [TYPE_GOAL_PROGRESS].
*/
- val rangedValueExpression: FloatExpression?
+ val rangedValueExpression: DynamicFloat?
get() {
checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE_EXPRESSION, type)
- return fields.getByteArray(FIELD_VALUE_EXPRESSION)?.toFloatExpression()
+ return fields.getByteArray(FIELD_VALUE_EXPRESSION)
+ ?.let { DynamicFloat.fromByteArray(it) }
}
/**
@@ -1185,7 +1186,8 @@
equalsWithoutExpressions(other) &&
(!isFieldValidForType(FIELD_VALUE, type) || rangedValue == other.rangedValue) &&
(!isFieldValidForType(FIELD_VALUE_EXPRESSION, type) ||
- rangedValueExpression == other.rangedValueExpression) &&
+ rangedValueExpression?.toDynamicFloatByteArray() contentEquals
+ other.rangedValueExpression?.toDynamicFloatByteArray()) &&
(!isFieldValidForType(FIELD_SHORT_TITLE, type) || shortTitle == other.shortTitle) &&
(!isFieldValidForType(FIELD_SHORT_TEXT, type) || shortText == other.shortText) &&
(!isFieldValidForType(FIELD_LONG_TITLE, type) || longTitle == other.longTitle) &&
@@ -1202,7 +1204,8 @@
) {
!isFieldValidForType(FIELD_VALUE, type) || rangedValue == other.rangedValue
} else {
- rangedValueExpression == other.rangedValueExpression
+ rangedValueExpression?.toDynamicFloatByteArray() contentEquals
+ other.rangedValueExpression?.toDynamicFloatByteArray()
} &&
(!isFieldValidForType(FIELD_SHORT_TITLE, type) ||
shortTitle equalsUnevaluated other.shortTitle) &&
@@ -1219,11 +1222,14 @@
((placeholder != null && other.placeholder != null) &&
placeholder!! equalsUnevaluated other.placeholder!!)))
- private infix fun ComplicationText?.equalsUnevaluated(other: ComplicationText?): Boolean =
- (this == null && other == null) ||
- ((this != null && other != null) &&
- if (stringExpression == null) equals(other)
- else stringExpression == other.stringExpression)
+ private infix fun ComplicationText?.equalsUnevaluated(other: ComplicationText?): Boolean {
+ if (this == null && other == null) return true
+ if (this == null || other == null) return false
+ // Both are non-null.
+ if (stringExpression == null) return equals(other)
+ return stringExpression?.toDynamicStringByteArray() contentEquals
+ other.stringExpression?.toDynamicStringByteArray()
+ }
private fun equalsWithoutExpressions(other: ComplicationData): Boolean =
this === other || (
@@ -1305,7 +1311,11 @@
if (isFieldValidForType(EXP_FIELD_LIST_ENTRIES, type)) listEntries else null,
if (isFieldValidForType(FIELD_DATA_SOURCE, type)) dataSource else null,
if (isFieldValidForType(FIELD_VALUE, type)) rangedValue else null,
- if (isFieldValidForType(FIELD_VALUE_EXPRESSION, type)) rangedValueExpression else null,
+ if (isFieldValidForType(FIELD_VALUE_EXPRESSION, type)) {
+ Arrays.hashCode(rangedValueExpression?.toDynamicFloatByteArray())
+ } else {
+ null
+ },
if (isFieldValidForType(FIELD_VALUE_TYPE, type)) rangedValueType else null,
if (isFieldValidForType(FIELD_MIN_VALUE, type)) rangedMinValue else null,
if (isFieldValidForType(FIELD_MAX_VALUE, type)) rangedMaxValue else null,
@@ -1467,8 +1477,8 @@
*
* @throws IllegalStateException if this field is not valid for the complication type
*/
- fun setRangedValueExpression(value: FloatExpression?) =
- apply { putOrRemoveField(FIELD_VALUE_EXPRESSION, value?.asByteArray()) }
+ fun setRangedValueExpression(value: DynamicFloat?) =
+ apply { putOrRemoveField(FIELD_VALUE_EXPRESSION, value?.toDynamicFloatByteArray()) }
/**
* Sets the *value type* field which provides meta data about the value. This is
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationText.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationText.java
index 58f43c9..cefbd65 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationText.java
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationText.java
@@ -36,8 +36,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-import androidx.wear.watchface.complications.data.StringExpression;
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString;
import java.io.IOException;
import java.io.InvalidObjectException;
@@ -46,6 +45,7 @@
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -76,17 +76,32 @@
return false;
}
ComplicationText that = (ComplicationText) o;
- if (!Objects.equals(mStringExpression, that.mStringExpression)) {
- return false;
+ if (mExpression == null) {
+ if (that.mExpression != null) {
+ return false;
+ }
+ } else {
+ if (that.mExpression == null) {
+ return false;
+ } else if (
+ !Arrays.equals(mExpression.toDynamicStringByteArray(),
+ that.mExpression.toDynamicStringByteArray())
+ ) {
+ return false;
+ }
}
if (!Objects.equals(mTimeDependentText, that.mTimeDependentText)) {
return false;
}
if (mSurroundingText == null) {
- return that.mSurroundingText == null;
- } else {
if (that.mSurroundingText != null) {
- return mSurroundingText.toString().contentEquals(that.mSurroundingText);
+ return false;
+ }
+ } else {
+ if (that.mSurroundingText == null) {
+ return false;
+ } else if (!mSurroundingText.toString().contentEquals(that.mSurroundingText)) {
+ return false;
}
}
return true;
@@ -94,16 +109,20 @@
@Override
public int hashCode() {
- return Objects.hash(mSurroundingText, mTimeDependentText, mStringExpression);
+ return Objects.hash(
+ mSurroundingText,
+ mTimeDependentText,
+ mExpression == null ?
+ null : Arrays.hashCode(mExpression.toDynamicStringByteArray()));
}
@NonNull
@Override
public String toString() {
- return "ComplicationText{" + "mSurroundingText="
- + ComplicationData.maybeRedact(mSurroundingText)
- + ", mTimeDependentText=" + mTimeDependentText + ", mStringExpression="
- + mStringExpression + "}";
+ return "ComplicationText{"
+ + "mSurroundingText=" + ComplicationData.maybeRedact(mSurroundingText)
+ + ", mTimeDependentText=" + mTimeDependentText
+ + ", mExpression=" + mExpression + "}";
}
/** @hide */
@@ -274,9 +293,9 @@
@Nullable
private final TimeDependentText mTimeDependentText;
- /** A [StringExpression] which will be evaluated by the system on the WatchFace's behalf. */
+ /** A {@link DynamicString} which will be evaluated by the system on the WatchFace's behalf. */
@Nullable
- private final StringExpression mStringExpression;
+ private final DynamicString mExpression;
/** Used to replace occurrences of ^1 with time dependent text and ignore ^[2-9]. */
private final CharSequence[] mTemplateValues =
@@ -290,29 +309,29 @@
private ComplicationText(
@Nullable CharSequence surroundingText,
@Nullable TimeDependentText timeDependentText,
- @Nullable StringExpression stringExpression) {
+ @Nullable DynamicString expression) {
mSurroundingText = surroundingText;
mTimeDependentText = timeDependentText;
- mStringExpression = stringExpression;
+ mExpression = expression;
checkFields();
}
public ComplicationText(@NonNull CharSequence surroundingText) {
- this(surroundingText, /* timeDependentText = */ null, /* stringExpression = */ null);
+ this(surroundingText, /* timeDependentText = */ null, /* expression = */ null);
}
public ComplicationText(
@NonNull CharSequence surroundingText, @NonNull TimeDependentText timeDependentText) {
- this(surroundingText, timeDependentText, /* stringExpression = */ null);
+ this(surroundingText, timeDependentText, /* expression = */ null);
}
public ComplicationText(
- @NonNull CharSequence surroundingText, @NonNull StringExpression stringExpression) {
- this(surroundingText, /* timeDependentText = */ null, stringExpression);
+ @NonNull CharSequence surroundingText, @NonNull DynamicString expression) {
+ this(surroundingText, /* timeDependentText = */ null, expression);
}
- public ComplicationText(@NonNull StringExpression stringExpression) {
- this(/* surroundingText = */ null, /* timeDependentText = */ null, stringExpression);
+ public ComplicationText(@NonNull DynamicString expression) {
+ this(/* surroundingText = */ null, /* timeDependentText = */ null, expression);
}
private ComplicationText(@NonNull Parcel in) {
@@ -320,9 +339,9 @@
mSurroundingText = bundle.getCharSequence(KEY_SURROUNDING_STRING);
if (bundle.containsKey(KEY_STRING_EXPRESSION)) {
- mStringExpression = new StringExpression(bundle.getByteArray(KEY_STRING_EXPRESSION));
+ mExpression = DynamicString.fromByteArray(bundle.getByteArray(KEY_STRING_EXPRESSION));
} else {
- mStringExpression = null;
+ mExpression = null;
}
if (bundle.containsKey(KEY_DIFFERENCE_STYLE)
@@ -355,23 +374,23 @@
private static class SerializedForm implements Serializable {
CharSequence mSurroundingText;
TimeDependentText mTimeDependentText;
- StringExpression mStringExpression;
+ DynamicString mExpression;
SerializedForm(@Nullable CharSequence surroundingText,
@Nullable TimeDependentText timeDependentText,
- @Nullable StringExpression stringExpression) {
+ @Nullable DynamicString expression) {
mSurroundingText = surroundingText;
mTimeDependentText = timeDependentText;
- mStringExpression = stringExpression;
+ mExpression = expression;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
CharSequenceSerializableHelper.writeToStream(mSurroundingText, oos);
oos.writeObject(mTimeDependentText);
- if (mStringExpression == null) {
+ if (mExpression == null) {
oos.writeInt(0);
} else {
- byte[] bytes = mStringExpression.asByteArray();
+ byte[] bytes = mExpression.toDynamicStringByteArray();
oos.writeInt(bytes.length);
oos.write(bytes);
}
@@ -382,22 +401,22 @@
mTimeDependentText = (TimeDependentText) ois.readObject();
int length = ois.readInt();
if (length == 0) {
- mStringExpression = null;
+ mExpression = null;
} else {
byte[] bytes = new byte[length];
ois.readFully(bytes);
- mStringExpression = new StringExpression(bytes);
+ mExpression = DynamicString.fromByteArray(bytes);
}
}
@SuppressLint("SyntheticAccessor")
Object readResolve() {
- return new ComplicationText(mSurroundingText, mTimeDependentText, mStringExpression);
+ return new ComplicationText(mSurroundingText, mTimeDependentText, mExpression);
}
}
Object writeReplace() {
- return new SerializedForm(mSurroundingText, mTimeDependentText, mStringExpression);
+ return new SerializedForm(mSurroundingText, mTimeDependentText, mExpression);
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
@@ -421,10 +440,9 @@
}
private void checkFields() {
- if (mSurroundingText == null && mTimeDependentText == null && mStringExpression == null) {
+ if (mSurroundingText == null && mTimeDependentText == null && mExpression == null) {
throw new IllegalStateException(
- "One of mSurroundingText, mTimeDependentText and mStringExpression must be"
- + " non-null");
+ "One of mSurroundingText, mTimeDependentText and mExpression must be non-null");
}
}
@@ -439,8 +457,8 @@
Bundle bundle = new Bundle();
bundle.putCharSequence(KEY_SURROUNDING_STRING, mSurroundingText);
- if (mStringExpression != null) {
- bundle.putByteArray(KEY_STRING_EXPRESSION, mStringExpression.asByteArray());
+ if (mExpression != null) {
+ bundle.putByteArray(KEY_STRING_EXPRESSION, mExpression.toDynamicStringByteArray());
}
if (mTimeDependentText != null) {
@@ -476,7 +494,7 @@
@NonNull
@RestrictTo(RestrictTo.Scope.LIBRARY)
public TimeDependentText getTimeDependentText() {
- if (mStringExpression != null) {
+ if (mExpression != null) {
throw new UnsupportedOperationException("getTimeDependentText not supported for "
+ "StringExpressions");
}
@@ -500,7 +518,7 @@
@NonNull
@Override
public CharSequence getTextAt(@NonNull Resources resources, long dateTimeMillis) {
- if (mStringExpression != null && mTimeDependentText == null && mSurroundingText == null) {
+ if (mExpression != null && mTimeDependentText == null && mSurroundingText == null) {
throw new UnsupportedOperationException("getTextAt not supported for "
+ "StringExpressions");
}
@@ -533,10 +551,10 @@
return mSurroundingText;
}
- /** Returns the {@link StringExpression} to be evaluated to display this text. */
+ /** Returns the {@link DynamicString} to be evaluated to display this text. */
@Nullable
- public StringExpression getStringExpression() {
- return mStringExpression;
+ public DynamicString getStringExpression() {
+ return mExpression;
}
/** Whether or not this is a placeholder. */
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index 90dc690..444a7d1 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -31,6 +31,7 @@
import androidx.annotation.IntDef
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
import androidx.wear.watchface.complications.data.GoalProgressComplicationData.Companion.PLACEHOLDER
import androidx.wear.watchface.complications.data.RangedValueComplicationData.Companion.PLACEHOLDER
import androidx.wear.watchface.complications.data.RangedValueComplicationData.Companion.TYPE_RATING
@@ -847,7 +848,7 @@
public class RangedValueComplicationData internal constructor(
public val value: Float,
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
- valueExpression: FloatExpression?,
+ valueExpression: DynamicFloat?,
public val min: Float,
public val max: Float,
public val monochromaticImage: MonochromaticImage?,
@@ -873,13 +874,13 @@
displayPolicy = displayPolicy
) {
/**
- * The [FloatExpression] optionally set by the data source. If present the system will
+ * The [DynamicFloat] optionally set by the data source. If present the system will
* dynamically evaluate this and store the result in [value]. Watch faces can typically ignore
* this field.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public val valueExpression: FloatExpression? = valueExpression
+ public val valueExpression: DynamicFloat? = valueExpression
/** @hide */
@IntDef(value = [TYPE_UNDEFINED, TYPE_RATING, TYPE_PERCENTAGE])
@@ -896,7 +897,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
private val value: Float,
- private val valueExpression: FloatExpression?,
+ private val valueExpression: DynamicFloat?,
private val min: Float,
private val max: Float,
private var contentDescription: ComplicationText
@@ -920,9 +921,9 @@
) : this(value, valueExpression = null, min, max, contentDescription)
/**
- * Creates a [Builder] for a [RangedValueComplicationData] with a [FloatExpression] value.
+ * Creates a [Builder] for a [RangedValueComplicationData] with a [DynamicFloat] value.
*
- * @param valueExpression The [FloatExpression] of the ranged complication which will be
+ * @param valueExpression The [DynamicFloat] of the ranged complication which will be
* evaluated into a value dynamically, and should be in the range [[min]] .. [[max]]. The
* semantic meaning of value can be specified via [setValueType].
* @param min The minimum value. For [TYPE_PERCENTAGE] this must be 0f.
@@ -934,7 +935,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
- valueExpression: FloatExpression,
+ valueExpression: DynamicFloat,
min: Float,
max: Float,
contentDescription: ComplicationText
@@ -1203,7 +1204,7 @@
internal constructor(
public val value: Float,
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
- valueExpression: FloatExpression?,
+ valueExpression: DynamicFloat?,
public val targetValue: Float,
public val monochromaticImage: MonochromaticImage?,
public val smallImage: SmallImage?,
@@ -1227,13 +1228,13 @@
displayPolicy = displayPolicy
) {
/**
- * The [FloatExpression] optionally set by the data source. If present the system will
+ * The [DynamicFloat] optionally set by the data source. If present the system will
* dynamically evaluate this and store the result in [value]. Watch faces can typically ignore
* this field.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public val valueExpression: FloatExpression? = valueExpression
+ public val valueExpression: DynamicFloat? = valueExpression
/**
* Builder for [GoalProgressComplicationData].
@@ -1247,7 +1248,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
private val value: Float,
- private val valueExpression: FloatExpression?,
+ private val valueExpression: DynamicFloat?,
private val targetValue: Float,
private var contentDescription: ComplicationText
) : BaseBuilder<Builder, GoalProgressComplicationData>() {
@@ -1266,9 +1267,9 @@
) : this(value, valueExpression = null, targetValue, contentDescription)
/**
- * Creates a [Builder] for a [GoalProgressComplicationData] with a [FloatExpression] value.
+ * Creates a [Builder] for a [GoalProgressComplicationData] with a [DynamicFloat] value.
*
- * @param valueExpression The [FloatExpression] of the goal complication which will be
+ * @param valueExpression The [DynamicFloat] of the goal complication which will be
* evaluated into a value dynamically, and should be >= 0.
* @param targetValue The target value. This must be less than [Float.MAX_VALUE].
* @param contentDescription Localized description for use by screen readers. Please do not
@@ -1277,7 +1278,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
- valueExpression: FloatExpression,
+ valueExpression: DynamicFloat,
targetValue: Float,
contentDescription: ComplicationText
) : this(
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/FloatExpression.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/FloatExpression.kt
deleted file mode 100644
index 5d2215e..0000000
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/FloatExpression.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.wear.watchface.complications.data
-
-import androidx.annotation.RestrictTo
-
-/**
- * Placeholder for FloatExpression implementation by tiles.
- * @hide
- */
-// TODO(b/260065006): Replace this with the real implementation.
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-abstract class FloatExpression {
- abstract fun asByteArray(): ByteArray
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- // Not checking for exact same class because it's not implemented yet.
- if (other !is FloatExpression) return false
- return asByteArray().contentEquals(other.asByteArray())
- }
-
- override fun hashCode() = asByteArray().contentHashCode()
-
- override fun toString() = "FloatExpressionPlaceholder${asByteArray().contentToString()}"
-}
-
-/**
- * Placeholder parser for [FloatExpression] from [ByteArray].
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun ByteArray.toFloatExpression() = object : FloatExpression() {
- override fun asByteArray() = this@toFloatExpression
-}
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index 5887dd6..22f7fc7 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -34,6 +34,7 @@
import android.text.style.TypefaceSpan
import android.text.style.UnderlineSpan
import androidx.annotation.RestrictTo
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import java.time.Instant
import java.util.concurrent.TimeUnit
@@ -628,43 +629,15 @@
DelegatingTimeDependentText(this)
/**
- * This is a placeholder for the tiles StringExpression which isn't currently available. We'll
- * remove this later in favor of the real thing.
- * @hide
- */
-// TODO(b/260065006): Remove this in favor of the real thing when available.
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class StringExpression(private val expression: ByteArray) {
- fun asByteArray() = expression
-
- override fun toString(): String {
- return "StringExpression(expression=${expression.contentToString()})"
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as StringExpression
-
- if (!expression.contentEquals(other.expression)) return false
-
- return true
- }
-
- override fun hashCode() = expression.contentHashCode()
-}
-
-/**
- * A [ComplicationText] where the system evaluates a [StringExpression] on behalf of the watch face.
+ * A [ComplicationText] where the system evaluates a [DynamicString] on behalf of the watch face.
* By the time this reaches the watch face's Renderer, it'll have been converted to a plain
* ComplicationText.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class StringExpressionComplicationText(
- public val expression: StringExpression
+public class ComplicationTextExpression(
+ public val expression: DynamicString
) : ComplicationText {
private val delegate = DelegatingComplicationText(WireComplicationText(expression))
@@ -695,7 +668,7 @@
if (this === other) return true
if (javaClass != other?.javaClass) return false
- other as StringExpressionComplicationText
+ other as ComplicationTextExpression
if (delegate != other.delegate) return false
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
index bde19c8..9dd28c8 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataEqualityTest.kt
@@ -24,11 +24,11 @@
import android.support.wearable.complications.ComplicationText.plainText
import android.util.Log
import androidx.test.core.app.ApplicationProvider
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
-import androidx.wear.watchface.complications.data.FloatExpression
import androidx.wear.watchface.complications.data.SharedRobolectricTestRunner
-import androidx.wear.watchface.complications.data.StringExpression
import com.google.common.truth.Expect
import kotlin.random.Random
import org.junit.Before
@@ -88,20 +88,8 @@
{ setRangedValue(2f) },
),
RANGED_VALUE_EXPRESSION(
- {
- setRangedValueExpression(
- object : FloatExpression() {
- override fun asByteArray() = byteArrayOf(1, 2)
- }
- )
- },
- {
- setRangedValueExpression(
- object : FloatExpression() {
- override fun asByteArray() = byteArrayOf(3, 4)
- }
- )
- },
+ { setRangedValueExpression(DynamicFloat.constant(1.2f)) },
+ { setRangedValueExpression(DynamicFloat.constant(3.4f)) },
),
RANGED_VALUE_TYPE(
{ setRangedValueType(1) },
@@ -352,19 +340,11 @@
RANGED_VALUE_EXPRESSION(
{
setRangedValue(Random.nextFloat()) // Ignored when there's an expression.
- .setRangedValueExpression(
- object : FloatExpression() {
- override fun asByteArray() = byteArrayOf(1, 2)
- }
- )
+ .setRangedValueExpression(DynamicFloat.constant(1.2f))
},
{
setRangedValue(Random.nextFloat()) // Ignored when there's an expression.
- .setRangedValueExpression(
- object : FloatExpression() {
- override fun asByteArray() = byteArrayOf(3, 4)
- }
- )
+ .setRangedValueExpression(DynamicFloat.constant(3.4f))
},
),
@@ -378,7 +358,7 @@
setShortTitle(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
},
@@ -386,7 +366,7 @@
setShortTitle(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
},
@@ -402,7 +382,7 @@
setShortText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
},
@@ -410,7 +390,7 @@
setShortText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
},
@@ -426,7 +406,7 @@
setLongTitle(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
},
@@ -434,7 +414,7 @@
setLongTitle(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
},
@@ -450,7 +430,7 @@
setLongText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
},
@@ -458,7 +438,7 @@
setLongText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
},
@@ -474,7 +454,7 @@
setContentDescription(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
},
@@ -482,7 +462,7 @@
setContentDescription(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
},
@@ -500,7 +480,7 @@
.setShortText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(1, 2))
+ DynamicString.constant("1")
)
)
.build()
@@ -512,7 +492,7 @@
.setShortText(
ComplicationText(
Random.nextInt().toString(), // Ignored when there's an expression.
- StringExpression(byteArrayOf(3, 4))
+ DynamicString.constant("2")
)
)
.build()
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
index a090595..617d51a 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
@@ -24,8 +24,8 @@
import android.support.wearable.complications.ComplicationText.TimeFormatBuilder
import android.support.wearable.complications.ComplicationText.plainText
import androidx.test.core.app.ApplicationProvider
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
import androidx.wear.watchface.complications.data.SharedRobolectricTestRunner
-import androidx.wear.watchface.complications.data.toFloatExpression
import com.google.common.truth.Truth.assertThat
import org.junit.Assert
import org.junit.Assert.assertThrows
@@ -100,7 +100,7 @@
// GIVEN complication data of the RANGED_VALUE type created by the Builder...
val data =
ComplicationData.Builder(ComplicationData.TYPE_RANGED_VALUE)
- .setRangedValueExpression(byteArrayOf(42, 107).toFloatExpression())
+ .setRangedValueExpression(DynamicFloat.constant(20f))
.setRangedMinValue(5f)
.setRangedMaxValue(150f)
.setShortTitle(plainText("title"))
@@ -109,7 +109,8 @@
// WHEN the relevant getters are called on the resulting data
// THEN the correct values are returned.
- assertThat(data.rangedValueExpression!!.asByteArray()).isEqualTo(byteArrayOf(42, 107))
+ assertThat(data.rangedValueExpression?.toDynamicFloatByteArray())
+ .isEqualTo(DynamicFloat.constant(20f).toDynamicFloatByteArray())
Assert.assertEquals(data.rangedMinValue, 5f, 0f)
Assert.assertEquals(data.rangedMaxValue, 150f, 0f)
assertThat(data.shortTitle!!.getTextAt(mResources, 0)).isEqualTo("title")
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationTextTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationTextTest.kt
index 3b3e6b9..331b996 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationTextTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationTextTest.kt
@@ -18,11 +18,13 @@
import android.content.Context
import android.os.Parcel
+import android.support.wearable.complications.ComplicationText.FORMAT_STYLE_DEFAULT
import android.support.wearable.complications.ComplicationText.TimeDifferenceBuilder
import android.support.wearable.complications.ComplicationText.TimeFormatBuilder
import androidx.test.core.app.ApplicationProvider
-import androidx.wear.watchface.complications.data.StringExpression
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import androidx.wear.watchface.complications.data.SharedRobolectricTestRunner
+import com.google.common.testing.EqualsTester
import com.google.common.truth.Truth
import org.junit.Assert
import org.junit.Test
@@ -36,6 +38,33 @@
private val mResources = ApplicationProvider.getApplicationContext<Context>().resources
@Test
+ public fun testEquality() {
+ fun dup(builder: () -> ComplicationText) = arrayOf(builder(), builder())
+ fun timeFormat(value: String) = TimeFormatText(value, FORMAT_STYLE_DEFAULT, null)
+
+ // Verifying all possible constructors, with duplicate values and different values for each
+ // constructor argument.
+ EqualsTester()
+ .addEqualityGroup(dup { ComplicationText("surrounding") })
+ .addEqualityGroup(dup { ComplicationText("surrounding 2") })
+ .addEqualityGroup(dup { ComplicationText("surrounding", timeFormat("%h")) })
+ .addEqualityGroup(dup { ComplicationText("surrounding 2", timeFormat("%h")) })
+ .addEqualityGroup(dup { ComplicationText("surrounding", timeFormat("%m")) })
+ .addEqualityGroup(dup { ComplicationText(DynamicString.constant("expression")) })
+ .addEqualityGroup(dup { ComplicationText(DynamicString.constant("expression 2")) })
+ .addEqualityGroup(
+ dup { ComplicationText("surrounding", DynamicString.constant("expression")) }
+ )
+ .addEqualityGroup(
+ dup { ComplicationText("surrounding 2", DynamicString.constant("expression")) }
+ )
+ .addEqualityGroup(
+ dup { ComplicationText("surrounding", DynamicString.constant("expression 2")) }
+ )
+ .testEquals()
+ }
+
+ @Test
public fun testPlainText() {
// GIVEN ComplicationText of the plain string type
val complicationText =
@@ -803,7 +832,7 @@
@Test
public fun stringExpressionToParcelRoundTrip() {
- val text = ComplicationText(StringExpression(byteArrayOf(1, 2, 3)))
+ val text = ComplicationText(DynamicString.constant("hello"))
Truth.assertThat(text.toParcelRoundTrip()).isEqualTo(text)
}
@@ -812,7 +841,7 @@
public fun getTextAt_ignoresStringExpressionIfSurroundingStringPresent() {
val text = ComplicationText(
"hello" as CharSequence,
- StringExpression(byteArrayOf(1, 2, 3))
+ DynamicString.constant("world")
)
Truth.assertThat(text.getTextAt(mResources, 132456789).toString())
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
index 5ca6603..0ed46d2 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
@@ -32,6 +32,8 @@
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.test.core.app.ApplicationProvider
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
@@ -151,11 +153,11 @@
assertThat(data.toString()).isEqualTo(
"ShortTextComplicationData(text=ComplicationText{mSurroundingText=text, " +
- "mTimeDependentText=null, mStringExpression=null}, title=ComplicationText{" +
- "mSurroundingText=title, mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, title=ComplicationText{" +
+ "mSurroundingText=title, mTimeDependentText=null, mExpression=null}, " +
"monochromaticImage=null, smallImage=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
+ "mExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=1, displayPolicy=1)"
@@ -204,13 +206,13 @@
assertThat(data.toString()).isEqualTo(
"ShortTextComplicationData(text=ComplicationText{mSurroundingText=text, " +
- "mTimeDependentText=null, mStringExpression=null}, title=ComplicationText{" +
- "mSurroundingText=title, mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, title=ComplicationText{" +
+ "mSurroundingText=title, mTimeDependentText=null, mExpression=null}, " +
"monochromaticImage=MonochromaticImage(image=Icon(typ=URI uri=someuri), " +
"ambientImage=null), smallImage=SmallImage(image=Icon(typ=URI uri=someuri2), " +
"type=PHOTO, ambientImage=null), contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
+ "mExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
@@ -248,11 +250,11 @@
assertThat(data.toString()).isEqualTo(
"LongTextComplicationData(text=ComplicationText{mSurroundingText=text, " +
- "mTimeDependentText=null, mStringExpression=null}, title=ComplicationText{" +
- "mSurroundingText=title, mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, title=ComplicationText{" +
+ "mSurroundingText=title, mTimeDependentText=null, mExpression=null}, " +
"monochromaticImage=null, smallImage=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
@@ -301,13 +303,13 @@
assertThat(data.toString()).isEqualTo(
"LongTextComplicationData(text=ComplicationText{mSurroundingText=text, " +
- "mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, " +
"title=ComplicationText{mSurroundingText=title, mTimeDependentText=null, " +
- "mStringExpression=null}, monochromaticImage=MonochromaticImage(" +
+ "mExpression=null}, monochromaticImage=MonochromaticImage(" +
"image=Icon(typ=URI uri=someuri), ambientImage=null), smallImage=SmallImage(" +
"image=Icon(typ=URI uri=someuri2), type=PHOTO, ambientImage=null), " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -353,9 +355,9 @@
"RangedValueComplicationData(value=95.0, valueExpression=null, valueType=0, " +
"min=0.0, max=100.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=battery, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
@@ -366,7 +368,7 @@
@Test
public fun rangedValueComplicationData_withValueExpression() {
val data = RangedValueComplicationData.Builder(
- valueExpression = byteArrayOf(42, 107).toFloatExpression(),
+ valueExpression = DynamicFloat.constant(20f),
min = 5f,
max = 100f,
contentDescription = "content description".complicationText
@@ -377,7 +379,7 @@
ParcelableSubject.assertThat(data.asWireComplicationData())
.hasSameSerializationAs(
WireComplicationData.Builder(WireComplicationData.TYPE_RANGED_VALUE)
- .setRangedValueExpression(byteArrayOf(42, 107).toFloatExpression())
+ .setRangedValueExpression(DynamicFloat.constant(20f))
.setRangedValue(5f) // min as a sensible default
.setRangedValueType(RangedValueComplicationData.TYPE_UNDEFINED)
.setRangedMinValue(5f)
@@ -393,7 +395,8 @@
val deserialized = serializeAndDeserialize(data) as RangedValueComplicationData
assertThat(deserialized.max).isEqualTo(100f)
assertThat(deserialized.min).isEqualTo(5f)
- assertThat(deserialized.valueExpression!!.asByteArray()).isEqualTo(byteArrayOf(42, 107))
+ assertThat(deserialized.valueExpression?.toDynamicFloatByteArray())
+ .isEqualTo(DynamicFloat.constant(20f).toDynamicFloatByteArray())
assertThat(deserialized.value).isEqualTo(5f) // min as a sensible default
assertThat(deserialized.contentDescription!!.getTextAt(resources, Instant.EPOCH))
.isEqualTo("content description")
@@ -402,12 +405,12 @@
assertThat(data.toString()).isEqualTo(
"RangedValueComplicationData(value=5.0, " +
- "valueExpression=FloatExpressionPlaceholder[42, 107], valueType=0, min=5.0, " +
+ "valueExpression=FixedFloat{value=20.0}, valueType=0, min=5.0, " +
"max=100.0, monochromaticImage=null, smallImage=null, title=ComplicationText{" +
- "mSurroundingText=battery, mTimeDependentText=null, mStringExpression=null}, " +
+ "mSurroundingText=battery, mTimeDependentText=null, mExpression=null}, " +
"text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
"ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
@@ -421,9 +424,7 @@
value = 95f, min = 0f, max = 100f,
contentDescription = "content description".complicationText
)
- .setTitle(
- StringExpressionComplicationText(StringExpression(byteArrayOf(1, 2, 3, 4, 5)))
- )
+ .setTitle(ComplicationTextExpression(DynamicString.constant("title")))
.setDataSource(dataSource)
.build()
ParcelableSubject.assertThat(data.asWireComplicationData())
@@ -433,9 +434,7 @@
.setRangedValueType(RangedValueComplicationData.TYPE_UNDEFINED)
.setRangedMinValue(0f)
.setRangedMaxValue(100f)
- .setShortTitle(
- WireComplicationText(StringExpression(byteArrayOf(1, 2, 3, 4, 5)))
- )
+ .setShortTitle(WireComplicationText(DynamicString.constant("title")))
.setContentDescription(WireComplicationText.plainText("content description"))
.setDataSource(dataSource)
.setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
@@ -454,9 +453,9 @@
"RangedValueComplicationData(value=95.0, valueExpression=null, valueType=0, " +
"min=0.0, max=100.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=(null), mTimeDependentText=null, " +
- "mStringExpression=StringExpression(expression=[1, 2, 3, 4, 5])}, text=null, " +
+ "mExpression=FixedString{value=title}}, text=null, " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -516,9 +515,9 @@
"monochromaticImage=MonochromaticImage(image=Icon(typ=URI uri=someuri), " +
"ambientImage=null), smallImage=SmallImage(image=Icon(typ=URI uri=someuri2), " +
"type=PHOTO, ambientImage=null), title=ComplicationText{mSurroundingText=battery," +
- " mTimeDependentText=null, mStringExpression=null}, text=null, " +
+ " mTimeDependentText=null, mExpression=null}, text=null, " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -562,9 +561,9 @@
"GoalProgressComplicationData(value=1200.0, valueExpression=null, " +
"targetValue=10000.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=steps, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
@@ -575,7 +574,7 @@
@Test
public fun goalProgressComplicationData_withValueExpression() {
val data = GoalProgressComplicationData.Builder(
- valueExpression = byteArrayOf(42, 107).toFloatExpression(),
+ valueExpression = DynamicFloat.constant(10f),
targetValue = 10000f,
contentDescription = "content description".complicationText
)
@@ -585,7 +584,7 @@
ParcelableSubject.assertThat(data.asWireComplicationData())
.hasSameSerializationAs(
WireComplicationData.Builder(WireComplicationData.TYPE_GOAL_PROGRESS)
- .setRangedValueExpression(byteArrayOf(42, 107).toFloatExpression())
+ .setRangedValueExpression(DynamicFloat.constant(10f))
.setRangedValue(0f) // sensible default
.setTargetValue(10000f)
.setShortTitle(WireComplicationText.plainText("steps"))
@@ -597,7 +596,8 @@
)
testRoundTripConversions(data)
val deserialized = serializeAndDeserialize(data) as GoalProgressComplicationData
- assertThat(deserialized.valueExpression!!.asByteArray()).isEqualTo(byteArrayOf(42, 107))
+ assertThat(deserialized.valueExpression?.toDynamicFloatByteArray())
+ .isEqualTo(DynamicFloat.constant(10f).toDynamicFloatByteArray())
assertThat(deserialized.value).isEqualTo(0f) // sensible default
assertThat(deserialized.targetValue).isEqualTo(10000f)
assertThat(deserialized.contentDescription!!.getTextAt(resources, Instant.EPOCH))
@@ -607,11 +607,11 @@
assertThat(data.toString()).isEqualTo(
"GoalProgressComplicationData(value=0.0, valueExpression=" +
- "FloatExpressionPlaceholder[42, 107], targetValue=10000.0, " +
+ "FixedFloat{value=10.0}, targetValue=10000.0, " +
"monochromaticImage=null, smallImage=null, title=ComplicationText{" +
- "mSurroundingText=steps, mTimeDependentText=null, mStringExpression=null}, " +
+ "mSurroundingText=steps, mTimeDependentText=null, mExpression=null}, " +
"text=null, contentDescription=ComplicationText{mSurroundingText=content " +
- "description, mTimeDependentText=null, mStringExpression=null}), " +
+ "description, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
"startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -657,9 +657,9 @@
"GoalProgressComplicationData(value=1200.0, valueExpression=null, " +
"targetValue=10000.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=steps, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=ColorRamp(colors=[-65536, " +
@@ -717,9 +717,9 @@
"monochromaticImage=MonochromaticImage(image=Icon(typ=URI uri=someuri), " +
"ambientImage=null), smallImage=SmallImage(image=Icon(typ=URI uri=someuri2), " +
"type=PHOTO, ambientImage=null), title=ComplicationText{mSurroundingText=steps, " +
- "mTimeDependentText=null, mStringExpression=null}, text=null, " +
+ "mTimeDependentText=null, mExpression=null}, text=null, " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -768,9 +768,9 @@
"RangedValueComplicationData(value=95.0, valueExpression=null, " +
"valueType=0, min=0.0, max=100.0, " +
"monochromaticImage=null, smallImage=null, title=ComplicationText{" +
- "mSurroundingText=battery, mTimeDependentText=null, mStringExpression=null}, " +
+ "mSurroundingText=battery, mTimeDependentText=null, mExpression=null}, " +
"text=null, contentDescription=ComplicationText{mSurroundingText=content " +
- "description, mTimeDependentText=null, mStringExpression=null}), " +
+ "description, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -839,9 +839,9 @@
" Element(color=-16711936, weight=1.0), Element(color=-16776961, weight=2.0), " +
"elementBackgroundColor=-7829368, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=calories, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
@@ -906,9 +906,9 @@
"image=Icon(typ=URI uri=someuri), ambientImage=null), " +
"smallImage=SmallImage(image=Icon(typ=URI uri=someuri2), type=PHOTO, " +
"ambientImage=null), title=ComplicationText{mSurroundingText=calories, " +
- "mTimeDependentText=null, mStringExpression=null}, text=null, " +
+ "mTimeDependentText=null, mExpression=null}, text=null, " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -943,7 +943,7 @@
"MonochromaticImageComplicationData(monochromaticImage=MonochromaticImage(" +
"image=Icon(typ=URI uri=someuri), ambientImage=null), contentDescription=" +
"ComplicationText{mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
@@ -980,7 +980,7 @@
"SmallImageComplicationData(smallImage=SmallImage(image=Icon(" +
"typ=URI uri=someuri), type=PHOTO, ambientImage=null), " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -1048,7 +1048,7 @@
assertThat(data.toString()).isEqualTo(
"PhotoImageComplicationData(photoImage=Icon(typ=URI uri=someuri), " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -1078,7 +1078,7 @@
assertThat(data.toString()).isEqualTo(
"NoPermissionComplicationData(text=ComplicationText{mSurroundingText=needs location, " +
- "mTimeDependentText=null, mStringExpression=null}, title=null, " +
+ "mTimeDependentText=null, mExpression=null}, title=null, " +
"monochromaticImage=null, smallImage=null, tapActionLostDueToSerialization=false," +
" tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
"-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
@@ -1115,7 +1115,7 @@
assertThat(data.toString()).isEqualTo(
"NoPermissionComplicationData(text=ComplicationText{" +
"mSurroundingText=needs location, mTimeDependentText=null, " +
- "mStringExpression=null}, title=null, monochromaticImage=MonochromaticImage(" +
+ "mExpression=null}, title=null, monochromaticImage=MonochromaticImage(" +
"image=Icon(typ=URI uri=someuri), ambientImage=null), smallImage=SmallImage(" +
"image=Icon(typ=URI uri=someuri2), type=PHOTO, ambientImage=null), " +
"tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
@@ -1165,13 +1165,13 @@
assertThat(data.toString()).isEqualTo(
"NoDataComplicationData(placeholder=ShortTextComplicationData(text=" +
"ComplicationText{mSurroundingText=__placeholder__, mTimeDependentText=null, " +
- "mStringExpression=null}, title=ComplicationText{" +
+ "mExpression=null}, title=ComplicationText{" +
"mSurroundingText=__placeholder__, mTimeDependentText=null, " +
- "mStringExpression=null}, monochromaticImage=MonochromaticImage(" +
+ "mExpression=null}, monochromaticImage=MonochromaticImage(" +
"image=Icon(typ=RESOURCE pkg= id=0xffffffff), ambientImage=null), " +
"smallImage=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
+ "mExpression=null}, tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
@@ -1216,9 +1216,9 @@
assertThat(data.toString()).isEqualTo(
"NoDataComplicationData(placeholder=LongTextComplicationData(" +
"text=ComplicationText{mSurroundingText=text, mTimeDependentText=null, " +
- "mStringExpression=null}, title=null, monochromaticImage=null, smallImage=null, " +
+ "mExpression=null}, title=null, monochromaticImage=null, smallImage=null, " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -1275,9 +1275,9 @@
"value=3.4028235E38, valueExpression=null, valueType=0, min=0.0, max=100.0, " +
"monochromaticImage=null, smallImage=null, title=null, text=ComplicationText{" +
"mSurroundingText=__placeholder__, mTimeDependentText=null, " +
- "mStringExpression=null}, contentDescription=ComplicationText{" +
+ "mExpression=null}, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
@@ -1333,9 +1333,9 @@
"value=3.4028235E38, valueExpression=null, targetValue=10000.0, " +
"monochromaticImage=null, smallImage=null, title=null, text=ComplicationText{" +
"mSurroundingText=__placeholder__, mTimeDependentText=null, " +
- "mStringExpression=null}, contentDescription=ComplicationText{" +
+ "mExpression=null}, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
@@ -1396,9 +1396,9 @@
"weight=1.0), Element(color=-16776961, weight=2.0), " +
"elementBackgroundColor=-7829368, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=calories, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
@@ -1458,9 +1458,9 @@
"value=3.4028235E38, valueExpression=null, valueType=1, min=0.0, max=100.0, " +
"monochromaticImage=null, smallImage=null, title=null, text=ComplicationText{" +
"mSurroundingText=__placeholder__, mTimeDependentText=null, " +
- "mStringExpression=null}, contentDescription=ComplicationText{" +
+ "mExpression=null}, contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=ColorRamp(colors=[-65536, " +
@@ -1508,7 +1508,7 @@
"monochromaticImage=MonochromaticImage(image=Icon(typ=RESOURCE pkg= " +
"id=0xffffffff), ambientImage=null), contentDescription=ComplicationText{" +
"mSurroundingText=content description, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
@@ -1555,7 +1555,7 @@
"NoDataComplicationData(placeholder=SmallImageComplicationData(smallImage=" +
"SmallImage(image=Icon(typ=RESOURCE pkg= id=0xffffffff), type=ICON, " +
"ambientImage=null), contentDescription=ComplicationText{mSurroundingText=" +
- "content description, mTimeDependentText=null, mStringExpression=null}), " +
+ "content description, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
"startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -1602,7 +1602,7 @@
"NoDataComplicationData(placeholder=PhotoImageComplicationData(" +
"photoImage=Icon(typ=RESOURCE pkg= id=0xffffffff), " +
"contentDescription=ComplicationText{mSurroundingText=content description, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
@@ -1714,7 +1714,7 @@
public fun rangedValueComplicationData_withValueExpression() {
assertRoundtrip(
WireComplicationData.Builder(WireComplicationData.TYPE_RANGED_VALUE)
- .setRangedValueExpression(byteArrayOf(42, 107).toFloatExpression())
+ .setRangedValueExpression(DynamicFloat.constant(10f))
.setRangedMinValue(0f)
.setRangedMaxValue(100f)
.setShortTitle(WireComplicationText.plainText("battery"))
@@ -1763,7 +1763,7 @@
public fun goalProgressComplicationData_withValueExpression() {
assertRoundtrip(
WireComplicationData.Builder(WireComplicationData.TYPE_GOAL_PROGRESS)
- .setRangedValueExpression(byteArrayOf(42, 107).toFloatExpression())
+ .setRangedValueExpression(DynamicFloat.constant(10f))
.setTargetValue(10000f)
.setShortTitle(WireComplicationText.plainText("steps"))
.setContentDescription(WireComplicationText.plainText("content description"))
@@ -2695,10 +2695,10 @@
assertThat(data.toString()).isEqualTo(
"ShortTextComplicationData(text=ComplicationText{mSurroundingText=REDACTED, " +
- "mTimeDependentText=null, mStringExpression=null}, title=ComplicationText{" +
- "mSurroundingText=REDACTED, mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, title=ComplicationText{" +
+ "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}, " +
"monochromaticImage=null, smallImage=null, contentDescription=ComplicationText{" +
- "mSurroundingText=REDACTED, mTimeDependentText=null, mStringExpression=null}, " +
+ "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}, " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
"displayPolicy=0)"
@@ -2719,10 +2719,10 @@
assertThat(data.toString()).isEqualTo(
"LongTextComplicationData(text=ComplicationText{mSurroundingText=REDACTED, " +
- "mTimeDependentText=null, mStringExpression=null}, title=ComplicationText{" +
- "mSurroundingText=REDACTED, mTimeDependentText=null, mStringExpression=null}, " +
+ "mTimeDependentText=null, mExpression=null}, title=ComplicationText{" +
+ "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}, " +
"monochromaticImage=null, smallImage=null, contentDescription=ComplicationText{" +
- "mSurroundingText=REDACTED, mTimeDependentText=null, mStringExpression=null}), " +
+ "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
"displayPolicy=0)"
@@ -2748,10 +2748,10 @@
"RangedValueComplicationData(value=REDACTED, valueExpression=REDACTED, " +
"valueType=0, min=0.0, max=100.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null, " +
- "mStringExpression=null}, text=ComplicationText{mSurroundingText=REDACTED, " +
- "mTimeDependentText=null, mStringExpression=null}, contentDescription=" +
+ "mExpression=null}, text=ComplicationText{mSurroundingText=REDACTED, " +
+ "mTimeDependentText=null, mExpression=null}, contentDescription=" +
"ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null, " +
- "mStringExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
+ "mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null," +
" validTimeRange=TimeRange(REDACTED), dataSource=null, colorRamp=null, " +
"persistencePolicy=0, displayPolicy=0)"
)
@@ -2775,8 +2775,8 @@
"GoalProgressComplicationData(value=REDACTED, valueExpression=REDACTED, " +
"targetValue=10000.0, monochromaticImage=null, smallImage=null, " +
"title=ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null, " +
- "mStringExpression=null}, text=null, contentDescription=ComplicationText{" +
- "mSurroundingText=REDACTED, mTimeDependentText=null, mStringExpression=null}), " +
+ "mExpression=null}, text=null, contentDescription=ComplicationText{" +
+ "mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
"TimeRange(REDACTED), dataSource=null, colorRamp=ColorRamp(colors=[-65536, " +
"-16711936, -16776961], interpolated=true), persistencePolicy=0, displayPolicy=0)"
@@ -2798,9 +2798,9 @@
assertThat(data.toString()).isEqualTo(
"NoDataComplicationData(placeholder=LongTextComplicationData(" +
"text=ComplicationText{mSurroundingText=__placeholder__, mTimeDependentText=null," +
- " mStringExpression=null}, title=null, monochromaticImage=null, smallImage=null, " +
+ " mExpression=null}, title=null, monochromaticImage=null, smallImage=null, " +
"contentDescription=ComplicationText{mSurroundingText=REDACTED, " +
- "mTimeDependentText=null, mStringExpression=null}), " +
+ "mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
"displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 84970d4..a90f3c4 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -6737,4 +6737,4 @@
override fun getSystemTimeZoneId() = ZoneId.of("UTC")
}
-}
\ No newline at end of file
+}