Merge "Update component id for room to internal id" into androidx-main
diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle
index 32d2f1e..e858364 100644
--- a/buildSrc/shared.gradle
+++ b/buildSrc/shared.gradle
@@ -53,11 +53,6 @@
implementation(project(":jetpad-integration")) // Doesn't have a .pom, so not slow to load
}
-// Exclude dokka coming from AGP. We don't need it and it conflicts with dackka: b/195305339
-configurations.configureEach { conf ->
- conf.exclude(group:"org.jetbrains.dokka", module:"dokka-core")
-}
-
// Saves configuration into destFile
// Each line of destFile will be the absolute filepath of one of the files in configuration
def saveConfigurationResolution(configuration, destFile) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
index ef7ccc3..47c4175 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
@@ -20,7 +20,6 @@
import android.graphics.Rect
import android.hardware.camera2.CameraCharacteristics
import android.util.Rational
-import android.util.Size
import androidx.annotation.RequiresApi
import androidx.arch.core.util.Function
import androidx.camera.camera2.pipe.CameraPipe
@@ -48,13 +47,13 @@
import androidx.camera.core.impl.utils.futures.FutureChain
import androidx.camera.core.impl.utils.futures.Futures
import com.google.common.util.concurrent.ListenableFuture
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.guava.asListenableFuture
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Adapt the [CameraControlInternal] interface to [CameraPipe].
@@ -165,7 +164,7 @@
return false
}
- override fun addZslConfig(resolution: Size, sessionConfigBuilder: SessionConfig.Builder) {
+ override fun addZslConfig(sessionConfigBuilder: SessionConfig.Builder) {
// Override if Zero-Shutter Lag needs to add config to session config.
}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
index e2d548e..aad2b48 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
@@ -796,8 +796,14 @@
@SdkSuppress(minSdkVersion = 23)
@Test
public void attachUseCaseWithTemplateZSLNoRecord() throws InterruptedException {
- UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
- UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
+ if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
+ return;
+ }
+ UseCase preview = createUseCase(
+ CameraDevice.TEMPLATE_PREVIEW,
+ /* isZslDisabled = */false);
+ UseCase zsl = createUseCase(
+ CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
/* isZslDisabled = */false);
mCamera2CameraImpl.attachUseCases(Arrays.asList(preview, zsl));
@@ -815,19 +821,24 @@
assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT))
.isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isFalse();
mCamera2CameraImpl.detachUseCases(Arrays.asList(preview, zsl));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
- .isFalse();
+ assertThat(mCamera2CameraImpl.getCameraControlInternal()
+ .isZslDisabledByByUserCaseConfig()).isFalse();
}
@SdkSuppress(minSdkVersion = 23)
@Test
public void attachUseCaseWithTemplateZSLHasRecord() throws InterruptedException {
- UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
+ if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
+ return;
+ }
+ UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
+ false);
UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, /* isZslDisabled = */true);
UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
/* isZslDisabled = */false);
@@ -848,19 +859,25 @@
assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT))
.isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isTrue();
mCamera2CameraImpl.detachUseCases(Arrays.asList(preview, record, zsl));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isFalse();
}
@SdkSuppress(minSdkVersion = 23)
@Test
public void attachAndDetachUseCasesMultipleTimes() throws InterruptedException {
- UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
+ if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
+ return;
+ }
+ UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
+ false);
UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, /* isZslDisabled = */true);
UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
/* isZslDisabled = */false);
@@ -870,40 +887,47 @@
mCamera2CameraImpl.onUseCaseActive(zsl);
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isFalse();
mCamera2CameraImpl.attachUseCases(Arrays.asList(record));
mCamera2CameraImpl.onUseCaseActive(record);
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isTrue();
mCamera2CameraImpl.detachUseCases(Arrays.asList(record));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isFalse();
mCamera2CameraImpl.attachUseCases(Arrays.asList(record));
mCamera2CameraImpl.onUseCaseActive(record);
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isTrue();
mCamera2CameraImpl.detachUseCases(Arrays.asList(zsl));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isTrue();
mCamera2CameraImpl.detachUseCases(Arrays.asList(preview));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isTrue();
mCamera2CameraImpl.detachUseCases(Arrays.asList(record));
HandlerUtil.waitForLooperToIdle(sCameraHandler);
- assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
+ assertThat(
+ mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
.isFalse();
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index fb33c76..d504a72 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -30,7 +30,6 @@
import android.os.Build;
import android.util.ArrayMap;
import android.util.Rational;
-import android.util.Size;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -390,9 +389,8 @@
}
@Override
- public void addZslConfig(@NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder) {
- mZslControl.addZslConfig(resolution, sessionConfigBuilder);
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
+ mZslControl.addZslConfig(sessionConfigBuilder);
}
@Override
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
index 5d78d06..c8550d6 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
@@ -17,7 +17,6 @@
package androidx.camera.camera2.internal;
import android.media.ImageWriter;
-import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -32,12 +31,9 @@
/**
* Adds zero-shutter lag config to {@link SessionConfig}.
*
- * @param resolution surface resolution.
* @param sessionConfigBuilder session config builder.
*/
- void addZslConfig(
- @NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder);
+ void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder);
/**
* Sets the flag if zero-shutter lag needs to be disabled by user case config.
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
index 2216bd41..12ecb98 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
@@ -16,14 +16,16 @@
package androidx.camera.camera2.internal;
+import static android.graphics.ImageFormat.PRIVATE;
import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
-import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
import static androidx.camera.camera2.internal.ZslUtil.isCapabilitySupported;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageWriter;
import android.os.Build;
@@ -45,10 +47,14 @@
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.ImmediateSurface;
import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.internal.compat.ImageWriterCompat;
import androidx.camera.core.internal.utils.ZslRingBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.NoSuchElementException;
/**
@@ -65,6 +71,12 @@
@VisibleForTesting
static final int MAX_IMAGES = RING_BUFFER_CAPACITY * 3;
+ @NonNull
+ private final Map<Integer, Size> mReprocessingInputSizeMap;
+
+ @NonNull
+ private final CameraCharacteristicsCompat mCameraCharacteristicsCompat;
+
@VisibleForTesting
@SuppressWarnings("WeakerAccess")
@NonNull
@@ -72,7 +84,6 @@
private boolean mIsZslDisabledByUseCaseConfig = false;
private boolean mIsZslDisabledByFlashMode = false;
- private boolean mIsYuvReprocessingSupported = false;
private boolean mIsPrivateReprocessingSupported = false;
@SuppressWarnings("WeakerAccess")
@@ -84,13 +95,13 @@
ImageWriter mReprocessingImageWriter;
ZslControlImpl(@NonNull CameraCharacteristicsCompat cameraCharacteristicsCompat) {
- mIsYuvReprocessingSupported =
- isCapabilitySupported(cameraCharacteristicsCompat,
- REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+ mCameraCharacteristicsCompat = cameraCharacteristicsCompat;
mIsPrivateReprocessingSupported =
- isCapabilitySupported(cameraCharacteristicsCompat,
+ isCapabilitySupported(mCameraCharacteristicsCompat,
REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+ mReprocessingInputSizeMap = createReprocessingInputSizeMap(mCameraCharacteristicsCompat);
+
mImageRingBuffer = new ZslRingBuffer(
RING_BUFFER_CAPACITY,
imageProxy -> imageProxy.close());
@@ -117,9 +128,7 @@
}
@Override
- public void addZslConfig(
- @NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder) {
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
cleanup();
// Early return only if use case config doesn't support zsl. If flash mode doesn't
@@ -130,15 +139,17 @@
return;
}
- if (!mIsYuvReprocessingSupported && !mIsPrivateReprocessingSupported) {
+ // Due to b/232268355 and feedback from pixel team that private format will have better
+ // performance, we will use private only for zsl.
+ if (!mIsPrivateReprocessingSupported
+ || mReprocessingInputSizeMap.isEmpty()
+ || !mReprocessingInputSizeMap.containsKey(PRIVATE)
+ || !isJpegValidOutputForInputFormat(mCameraCharacteristicsCompat, PRIVATE)) {
return;
}
- // Init the reprocessing image reader and enqueue available images into the ring buffer.
- // TODO(b/226683183): Decide whether YUV or PRIVATE reprocessing should be the default.
- int reprocessingImageFormat = mIsPrivateReprocessingSupported
- ? ImageFormat.PRIVATE : ImageFormat.YUV_420_888;
-
+ int reprocessingImageFormat = PRIVATE;
+ Size resolution = mReprocessingInputSizeMap.get(reprocessingImageFormat);
MetadataImageReader metadataImageReader = new MetadataImageReader(
resolution.getWidth(),
resolution.getHeight(),
@@ -258,4 +269,53 @@
mReprocessingImageWriter = null;
}
}
+
+ @NonNull
+ private Map<Integer, Size> createReprocessingInputSizeMap(
+ @NonNull CameraCharacteristicsCompat cameraCharacteristicsCompat) {
+ StreamConfigurationMap map =
+ cameraCharacteristicsCompat.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ if (map == null || map.getInputFormats() == null) {
+ return new HashMap<>();
+ }
+
+ Map<Integer, Size> inputSizeMap = new HashMap<>();
+ for (int format: map.getInputFormats()) {
+ Size[] inputSizes = map.getInputSizes(format);
+ if (inputSizes != null) {
+ // Sort by descending order
+ Arrays.sort(inputSizes, new CompareSizesByArea(true));
+
+ // TODO(b/233696144): Check if selecting an input size closer to output size will
+ // improve performance or not.
+ inputSizeMap.put(format, inputSizes[0]);
+ }
+ }
+ return inputSizeMap;
+ }
+
+ private boolean isJpegValidOutputForInputFormat(
+ @NonNull CameraCharacteristicsCompat cameraCharacteristicsCompat,
+ int inputFormat) {
+ StreamConfigurationMap map =
+ cameraCharacteristicsCompat.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ if (map == null) {
+ return false;
+ }
+
+ int[] validOutputFormats = map.getValidOutputFormatsForInput(inputFormat);
+ if (validOutputFormats == null) {
+ return false;
+ }
+ for (int outputFormat : validOutputFormats) {
+ if (outputFormat == ImageFormat.JPEG) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
index 715e70a..9ac407e 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
@@ -16,8 +16,6 @@
package androidx.camera.camera2.internal;
-import android.util.Size;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy;
@@ -28,8 +26,7 @@
*/
public class ZslControlNoOpImpl implements ZslControl {
@Override
- public void addZslConfig(@NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder) {
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
}
@Override
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
index 699662a..33ddaf5 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
@@ -16,10 +16,12 @@
package androidx.camera.camera2.internal
+import android.graphics.ImageFormat.JPEG
import android.graphics.ImageFormat.PRIVATE
import android.graphics.ImageFormat.YUV_420_888
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.params.StreamConfigurationMap
import android.os.Build
import android.util.Size
import androidx.camera.camera2.internal.ZslControlImpl.MAX_IMAGES
@@ -30,13 +32,16 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowCameraCharacteristics
-private val RESOLUTION = Size(640, 480)
+val YUV_REPROCESSING_MAXIMUM_SIZE = Size(4000, 3000)
+val PRIVATE_REPROCESSING_MAXIMUM_SIZE = Size(3000, 2000)
/**
* Unit tests for [ZslControlImpl].
@@ -57,64 +62,81 @@
}
@Test
- public fun isYuvReprocessingSupported_addZslConfig() {
- zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
- hasCapabilities = true,
- isYuvReprocessingSupported = true,
- isPrivateReprocessingSupported = false
- ))
-
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
-
- assertThat(zslControl.mReprocessingImageReader).isNotNull()
- assertThat(zslControl.mReprocessingImageReader.imageFormat).isEqualTo(YUV_420_888)
- assertThat(zslControl.mReprocessingImageReader.maxImages).isEqualTo(
- MAX_IMAGES)
- assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(
- RING_BUFFER_CAPACITY)
- }
-
- @Test
public fun isPrivateReprocessingSupported_addZslConfig() {
zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
hasCapabilities = true,
isYuvReprocessingSupported = false,
- isPrivateReprocessingSupported = true
+ isPrivateReprocessingSupported = true,
+ isJpegValidOutputFormat = true
))
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
assertThat(zslControl.mReprocessingImageReader).isNotNull()
assertThat(zslControl.mReprocessingImageReader.imageFormat).isEqualTo(PRIVATE)
assertThat(zslControl.mReprocessingImageReader.maxImages).isEqualTo(
MAX_IMAGES)
+ assertThat(zslControl.mReprocessingImageReader.width).isEqualTo(
+ PRIVATE_REPROCESSING_MAXIMUM_SIZE.width)
+ assertThat(zslControl.mReprocessingImageReader.height).isEqualTo(
+ PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(
RING_BUFFER_CAPACITY)
}
@Test
+ public fun isYuvReprocessingSupported_notAddZslConfig() {
+ zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
+ hasCapabilities = true,
+ isYuvReprocessingSupported = true,
+ isPrivateReprocessingSupported = false,
+ isJpegValidOutputFormat = true
+ ))
+
+ zslControl.addZslConfig(sessionConfigBuilder)
+
+ assertThat(zslControl.mReprocessingImageReader).isNull()
+ }
+
+ @Test
+ public fun isJpegNotValidOutputFormat_notAddZslConfig() {
+ zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
+ hasCapabilities = true,
+ isYuvReprocessingSupported = true,
+ isPrivateReprocessingSupported = false,
+ isJpegValidOutputFormat = false
+ ))
+
+ zslControl.addZslConfig(sessionConfigBuilder)
+
+ assertThat(zslControl.mReprocessingImageReader).isNull()
+ }
+
+ @Test
public fun isReprocessingNotSupported_notAddZslConfig() {
zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
hasCapabilities = true,
isYuvReprocessingSupported = false,
- isPrivateReprocessingSupported = false
+ isPrivateReprocessingSupported = false,
+ isJpegValidOutputFormat = false
))
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
assertThat(zslControl.mReprocessingImageReader).isNull()
}
@Test
- public fun isZslDisabledByUserCaseConfig_NotAddZslConfig() {
+ public fun isZslDisabledByUserCaseConfig_notAddZslConfig() {
zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
hasCapabilities = true,
isYuvReprocessingSupported = false,
- isPrivateReprocessingSupported = true
+ isPrivateReprocessingSupported = true,
+ isJpegValidOutputFormat = true
))
zslControl.isZslDisabledByUserCaseConfig = true
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
assertThat(zslControl.mReprocessingImageReader).isNull()
}
@@ -124,16 +146,21 @@
zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
hasCapabilities = true,
isYuvReprocessingSupported = false,
- isPrivateReprocessingSupported = true
+ isPrivateReprocessingSupported = true,
+ isJpegValidOutputFormat = true
))
zslControl.isZslDisabledByFlashMode = true
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
assertThat(zslControl.mReprocessingImageReader).isNotNull()
assertThat(zslControl.mReprocessingImageReader.imageFormat).isEqualTo(PRIVATE)
assertThat(zslControl.mReprocessingImageReader.maxImages).isEqualTo(
MAX_IMAGES)
+ assertThat(zslControl.mReprocessingImageReader.width).isEqualTo(
+ PRIVATE_REPROCESSING_MAXIMUM_SIZE.width)
+ assertThat(zslControl.mReprocessingImageReader.height).isEqualTo(
+ PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(
RING_BUFFER_CAPACITY)
}
@@ -143,13 +170,14 @@
zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
hasCapabilities = true,
isYuvReprocessingSupported = false,
- isPrivateReprocessingSupported = true
+ isPrivateReprocessingSupported = true,
+ isJpegValidOutputFormat = true
))
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
zslControl.isZslDisabledByUserCaseConfig = true
- zslControl.addZslConfig(RESOLUTION, sessionConfigBuilder)
+ zslControl.addZslConfig(sessionConfigBuilder)
assertThat(zslControl.mReprocessingImageReader).isNull()
}
@@ -157,7 +185,8 @@
private fun createCameraCharacteristicsCompat(
hasCapabilities: Boolean,
isYuvReprocessingSupported: Boolean,
- isPrivateReprocessingSupported: Boolean
+ isPrivateReprocessingSupported: Boolean,
+ isJpegValidOutputFormat: Boolean
): CameraCharacteristicsCompat {
val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics)
@@ -177,6 +206,47 @@
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES,
capabilities.toIntArray()
)
+
+ // Input formats
+ val streamConfigurationMap: StreamConfigurationMap = mock(
+ StreamConfigurationMap::class.java)
+
+ if (isYuvReprocessingSupported && isPrivateReprocessingSupported) {
+ Mockito.`when`(streamConfigurationMap.inputFormats).thenReturn(
+ arrayOf(YUV_420_888, PRIVATE).toIntArray()
+ )
+ Mockito.`when`(streamConfigurationMap.getInputSizes(YUV_420_888)).thenReturn(
+ arrayOf(YUV_REPROCESSING_MAXIMUM_SIZE)
+ )
+ Mockito.`when`(streamConfigurationMap.getInputSizes(PRIVATE)).thenReturn(
+ arrayOf(PRIVATE_REPROCESSING_MAXIMUM_SIZE)
+ )
+ } else if (isYuvReprocessingSupported) {
+ Mockito.`when`(streamConfigurationMap.inputFormats).thenReturn(
+ arrayOf(YUV_420_888).toIntArray()
+ )
+ Mockito.`when`(streamConfigurationMap.getInputSizes(YUV_420_888)).thenReturn(
+ arrayOf(YUV_REPROCESSING_MAXIMUM_SIZE)
+ )
+ } else if (isPrivateReprocessingSupported) {
+ Mockito.`when`(streamConfigurationMap.inputFormats).thenReturn(
+ arrayOf(PRIVATE).toIntArray()
+ )
+ Mockito.`when`(streamConfigurationMap.getInputSizes(PRIVATE)).thenReturn(
+ arrayOf(PRIVATE_REPROCESSING_MAXIMUM_SIZE)
+ )
+ }
+
+ // Output formats for input
+ if (isJpegValidOutputFormat) {
+ Mockito.`when`(streamConfigurationMap.getValidOutputFormatsForInput(PRIVATE))
+ .thenReturn(arrayOf(JPEG).toIntArray())
+ Mockito.`when`(streamConfigurationMap.getValidOutputFormatsForInput(YUV_420_888))
+ .thenReturn(arrayOf(JPEG).toIntArray())
+ }
+
+ shadowCharacteristics.set(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, streamConfigurationMap)
}
return CameraCharacteristicsCompat.toCameraCharacteristicsCompat(characteristics)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index ba945df..c24f28d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -375,7 +375,7 @@
YuvToJpegProcessor softwareJpegProcessor = null;
if (Build.VERSION.SDK_INT >= 23 && getCaptureMode() == CAPTURE_MODE_ZERO_SHUTTER_LAG) {
- getCameraControl().addZslConfig(resolution, sessionConfigBuilder);
+ getCameraControl().addZslConfig(sessionConfigBuilder);
}
// Setup the ImageReader to do processing
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
index 36c1f4d..33688e9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraControlInternal.java
@@ -19,7 +19,6 @@
import static androidx.camera.core.ImageCapture.FLASH_MODE_OFF;
import android.graphics.Rect;
-import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -60,12 +59,9 @@
/**
* Adds zero-shutter lag config to {@link SessionConfig}.
- * @param resolution surface resolution.
* @param sessionConfigBuilder session config builder.
*/
- void addZslConfig(
- @NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder);
+ void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder);
/**
* Sets the flag if zero-shutter lag needs to be disabled by user case config.
@@ -161,8 +157,7 @@
}
@Override
- public void addZslConfig(@NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder) {
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
}
@NonNull
diff --git a/camera/camera-effects/api/current.txt b/camera/camera-effects/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects/api/public_plus_experimental_current.txt b/camera/camera-effects/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects/api/res-current.txt b/camera/camera-effects/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-effects/api/res-current.txt
diff --git a/camera/camera-effects/api/restricted_current.txt b/camera/camera-effects/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects/build.gradle b/camera/camera-effects/build.gradle
new file mode 100644
index 0000000..e82c7eb
--- /dev/null
+++ b/camera/camera-effects/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import androidx.build.Publish
+import androidx.build.RunApiTasks
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+dependencies {
+ api(project(":camera:camera-core"))
+}
+android {
+ defaultConfig {
+ multiDexEnabled = true
+ }
+ testOptions.unitTests.includeAndroidResources = true
+}
+androidx {
+ name = "Jetpack Camera Effects Library"
+ publish = Publish.NONE
+ mavenGroup = LibraryGroups.CAMERA
+ inceptionYear = "2022"
+ runApiTasks = new RunApiTasks.Yes()
+ description = "Camera effects components for the Jetpack Camera Library, a library providing " +
+ "camera post-processing features such as portrait mode that can be used with the " +
+ "CameraX library."
+}
diff --git a/camera/camera-effects/src/main/AndroidManifest.xml b/camera/camera-effects/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f0be73e
--- /dev/null
+++ b/camera/camera-effects/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<manifest package="androidx.camera.effects"/>
\ No newline at end of file
diff --git a/camera/camera-effects/src/main/java/androidx/camera/effects/Portrait.java b/camera/camera-effects/src/main/java/androidx/camera/effects/Portrait.java
new file mode 100644
index 0000000..0284490
--- /dev/null
+++ b/camera/camera-effects/src/main/java/androidx/camera/effects/Portrait.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.effects;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Provides a portrait post-processing effect.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class Portrait {
+ // TODO: implement this
+}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index 6330c4b..0d2a5cb 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -141,8 +141,7 @@
}
@Override
- public void addZslConfig(@NonNull Size resolution,
- @NonNull SessionConfig.Builder sessionConfigBuilder) {
+ public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
// Override if Zero-Shutter Lag needs to add config to session config.
mIsZslConfigAdded = true;
}
diff --git a/camera/integration-tests/diagnosetestapp/build.gradle b/camera/integration-tests/diagnosetestapp/build.gradle
new file mode 100644
index 0000000..181ce2a
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("kotlin-android")
+ id("AndroidXComposePlugin")
+}
+
+android {
+ defaultConfig {
+ applicationId "androidx.camera.integration.diagnose"
+ minSdkVersion 21
+ multiDexEnabled true
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ }
+ }
+ namespace "androidx.camera.integration.diagnose"
+}
+
+dependencies {
+ implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
+
+ // Internal library
+ implementation(project(":camera:camera-camera2"))
+ implementation(project(":camera:camera-view"))
+ implementation(project(":camera:camera-lifecycle"))
+
+ // Android Support Library
+ implementation("androidx.appcompat:appcompat:1.3.0")
+ implementation("androidx.activity:activity-ktx:1.2.0")
+
+ implementation(libs.guavaAndroid)
+ implementation(libs.constraintLayout)
+ implementation 'androidx.camera:camera-view:1.1.0-beta03'
+
+ compileOnly(libs.kotlinCompiler)
+
+ // Testing framework
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.testUiautomator)
+ androidTestImplementation(libs.espressoCore)
+ androidTestImplementation(libs.truth)
+}
diff --git a/camera/integration-tests/diagnosetestapp/src/main/AndroidManifest.xml b/camera/integration-tests/diagnosetestapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d612196
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.camera.integration.diagnose">>
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-feature android:name="android.hardware.camera.autofocus" />
+
+ <application
+ android:icon="@drawable/ic_baseline_camera_enhance_24"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/camera/integration-tests/diagnosetestapp/src/main/java/androidx/camera/integration/diagnose/MainActivity.kt b/camera/integration-tests/diagnosetestapp/src/main/java/androidx/camera/integration/diagnose/MainActivity.kt
new file mode 100644
index 0000000..be73f4b
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/java/androidx/camera/integration/diagnose/MainActivity.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.camera.integration.diagnose
+
+import android.os.Bundle
+import android.widget.Button
+import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.view.LifecycleCameraController
+import androidx.camera.view.PreviewView
+
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var cameraController: LifecycleCameraController
+ private lateinit var previewView: PreviewView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ // TODO: request CAMERA permission and fail gracefully if not granted.
+
+ // Setup CameraX
+ previewView = findViewById(R.id.preview_view)
+ cameraController = LifecycleCameraController(this)
+ cameraController.bindToLifecycle(this)
+ previewView.controller = cameraController
+
+ // Setup UI events
+ findViewById<Button>(R.id.capture).setOnClickListener {
+ // TODO: handle capture button click event following examples
+ // in CameraControllerFragment.
+ }
+ }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/diagnosetestapp/src/main/res/drawable/ic_baseline_camera_enhance_24.xml b/camera/integration-tests/diagnosetestapp/src/main/res/drawable/ic_baseline_camera_enhance_24.xml
new file mode 100644
index 0000000..4e12ff4
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/res/drawable/ic_baseline_camera_enhance_24.xml
@@ -0,0 +1,6 @@
+<vector android:height="24dp" android:tint="#000000"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M9,3L7.17,5L4,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2h-3.17L15,3L9,3zM12,18c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+ <path android:fillColor="@android:color/white" android:pathData="M12,17l1.25,-2.75L16,13l-2.75,-1.25L12,9l-1.25,2.75L8,13l2.75,1.25z"/>
+</vector>
diff --git a/camera/integration-tests/diagnosetestapp/src/main/res/layout/activity_main.xml b/camera/integration-tests/diagnosetestapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..d9f6d47
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <FrameLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ <androidx.camera.view.PreviewView
+ android:id="@+id/preview_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/capture"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/capture"
+ android:checked="true"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/camera/integration-tests/diagnosetestapp/src/main/res/values/colors.xml b/camera/integration-tests/diagnosetestapp/src/main/res/values/colors.xml
new file mode 100644
index 0000000..558e3dc
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<resources>
+</resources>
\ No newline at end of file
diff --git a/camera/integration-tests/diagnosetestapp/src/main/res/values/strings.xml b/camera/integration-tests/diagnosetestapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7f32560
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ 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.
+ -->
+
+<resources>
+ <string name="app_name">diagnosetestapp</string>
+ <string name="capture">capture</string>
+</resources>
\ No newline at end of file
diff --git a/camera/integration-tests/diagnosetestapp/src/main/res/values/themes.xml b/camera/integration-tests/diagnosetestapp/src/main/res/values/themes.xml
new file mode 100644
index 0000000..1240763
--- /dev/null
+++ b/camera/integration-tests/diagnosetestapp/src/main/res/values/themes.xml
@@ -0,0 +1,19 @@
+<!--
+ 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.
+ -->
+
+<resources>
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
+</resources>
\ No newline at end of file
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java
index bc8ca3a..c3d6c17 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java
@@ -136,7 +136,7 @@
new CarIcon.Builder(
IconCompat.createWithResource(
getCarContext(),
- R.drawable.ic_fastfood_white_48dp))
+ R.drawable.ic_fastfood_yellow_48dp))
.build(),
mImageType)
.setOnClickListener(
diff --git a/car/app/app-samples/showcase/common/src/main/res/drawable/ic_fastfood_yellow_48dp.xml b/car/app/app-samples/showcase/common/src/main/res/drawable/ic_fastfood_yellow_48dp.xml
new file mode 100644
index 0000000..cbce92b
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/res/drawable/ic_fastfood_yellow_48dp.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:pathData="M36.12,45.98h3.32c1.69,0 3.07,-1.27 3.26,-2.92L46,10.1L36,10.1L36,2h-3.94v8.1h-9.94l0.6,4.68c3.43,0.94 6.63,2.64 8.53,4.52 2.88,2.85 4.87,5.77 4.87,10.59L36.12,46v-0.02zM2,43.98L2,42h30.06v1.98c0,1.1 -0.9,2 -2.02,2L4.02,45.98c-1.12,0 -2.02,-0.91 -2.02,-2zM32.06,29.98c0,-16 -30.06,-16 -30.06,0h30.06zM2.04,34h30v4h-30z"
+ android:fillColor="#FFFF00"/>
+</vector>
diff --git a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
index 5f7031d..1ad4377 100644
--- a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
+++ b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
@@ -137,8 +137,9 @@
get() = WindowInsetsHolder.current().displayCutout
/**
- * For the [WindowInsetsCompat.Type.ime]. On [Build.VERSION_CODES.R] and above, the
- * soft keyboard can be detected and [ime] will animate when it shows.
+ * For the [WindowInsetsCompat.Type.ime]. On API level 23 (M) and above, the soft keyboard can be
+ * detected and [ime] will update when it shows. On API 30 (R) and above, the [ime] insets will
+ * animate synchronously with the actual IME animation.
*
* Developers should set `android:windowSoftInputMode="adjustResize"` in their
* `AndroidManifest.xml` file and call `WindowCompat.setDecorFitsSystemWindows(window, false)`
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
index 04d445d..0edb218 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
@@ -65,5 +65,6 @@
ComposableDemo("Custom Touch Slop Demo - Composition Locals") { CustomTouchSlopSample() },
ComposableDemo("Focused bounds") { FocusedBoundsDemo() },
ComposableDemo("Scrollable with focused child") { ScrollableFocusedChildDemo() },
+ ComposableDemo("Window insets") { WindowInsetsDemo() },
)
)
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt
new file mode 100644
index 0000000..22000fb
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.demos
+
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment.Companion.CenterHorizontally
+import androidx.compose.ui.Alignment.Companion.CenterVertically
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun WindowInsetsDemo() {
+ val insets = WindowInsets.safeDrawing
+ val density = LocalDensity.current
+
+ Column {
+ Text(
+ "The numbers around the text field below show the respective WindowInsets values for" +
+ " the safeDrawing insets. To use this demo, go the demo app settings (⚙️ icon), " +
+ "set the soft input mode to AdjustResize, and disable Decor Fits System Windows." +
+ " If you don't configure the settings this way, the insets will not be updated." +
+ " Note that IME insets are only supported on API 23 and above."
+ )
+
+ BasicTextField(
+ value = "Click to show keyboard",
+ onValueChange = {},
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(),
+ textStyle = TextStyle(color = Color.Black.copy(alpha = 0.5f))
+ ) { field ->
+ with(density) {
+ Column(horizontalAlignment = CenterHorizontally) {
+ Text(
+ insets.getTop(density).toDp().toString(),
+ style = MaterialTheme.typography.caption
+ )
+ Row(verticalAlignment = CenterVertically) {
+ val layoutDirection = LocalLayoutDirection.current
+ Text(
+ insets.getLeft(density, layoutDirection).toDp().toString(),
+ style = MaterialTheme.typography.caption
+ )
+ Box(
+ Modifier
+ .padding(2.dp)
+ .border(1.dp, Color.Black)
+ ) {
+ field()
+ }
+ Text(
+ insets.getRight(density, layoutDirection).toDp().toString(),
+ style = MaterialTheme.typography.caption
+ )
+ }
+ Text(
+ insets.getBottom(density).toDp().toString(),
+ style = MaterialTheme.typography.caption
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
index c66bd05..6644869 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
@@ -412,6 +412,41 @@
}
@Test
+ fun verticalGrid_largeContentPaddingAndReverseLayout() {
+ val topPadding = itemSize * 2
+ val bottomPadding = itemSize * 2
+ val listSize = itemSize * 3
+ lateinit var state: LazyGridState
+ rule.setContentWithTestViewConfiguration {
+ LazyVerticalGrid(
+ GridCells.Fixed(1),
+ reverseLayout = true,
+ state = rememberLazyGridState().also { state = it },
+ modifier = Modifier.size(listSize),
+ contentPadding = PaddingValues(top = topPadding, bottom = bottomPadding),
+ ) {
+ items(3) { index ->
+ Box(Modifier.size(itemSize).testTag("$index"))
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertTopPositionInRootIsEqualTo(0.dp)
+ // Not visible.
+ rule.onNodeWithTag("1")
+ .assertIsNotDisplayed()
+
+ // Scroll to the top.
+ state.scrollBy(itemSize * 5f)
+
+ rule.onNodeWithTag("2").assertTopPositionInRootIsEqualTo(topPadding)
+ // Shouldn't be visible
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ rule.onNodeWithTag("0").assertIsNotDisplayed()
+ }
+
+ @Test
fun column_overscrollWithContentPadding() {
lateinit var state: LazyGridState
rule.setContent {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
index 98054cb..29c3a644 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
@@ -386,6 +386,42 @@
}
@Test
+ fun contentLargePaddingAndReverseLayout() {
+ val topPadding = itemSize * 2
+ val bottomPadding = itemSize * 2
+ val listSize = itemSize * 3
+ lateinit var state: LazyListState
+ rule.setContentWithTestViewConfiguration {
+ LazyColumnOrRow(
+ reverseLayout = true,
+ state = rememberLazyListState().also { state = it },
+ modifier = Modifier.requiredSize(listSize),
+ contentPadding = PaddingValues(
+ beforeContent = topPadding,
+ afterContent = bottomPadding
+ ),
+ ) {
+ items(3) { index ->
+ Box(Modifier.requiredSize(itemSize).testTag("$index"))
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertStartPositionInRootIsEqualTo(0.dp)
+ // Shouldn't be visible
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+
+ // Scroll to the top.
+ state.scrollBy(itemSize * 5f)
+
+ rule.onNodeWithTag("2").assertStartPositionInRootIsEqualTo(topPadding)
+ // Shouldn't be visible
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ rule.onNodeWithTag("0").assertIsNotDisplayed()
+ }
+
+ @Test
fun overscrollWithContentPadding() {
lateinit var state: LazyListState
rule.setContent {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
index 788671a..0b6ea3e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
@@ -23,6 +23,7 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
@@ -31,6 +32,7 @@
import androidx.compose.ui.platform.debugInspectorInfo
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
/**
* Configure component to be hoverable via pointer enter/exit events.
@@ -51,6 +53,7 @@
properties["enabled"] = enabled
}
) {
+ val scope = rememberCoroutineScope()
var hoverInteraction by remember { mutableStateOf<HoverInteraction.Enter?>(null) }
suspend fun emitEnter() {
@@ -96,11 +99,13 @@
// LocalPointerPosition.current
.pointerInput(interactionSource) {
val currentContext = currentCoroutineContext()
- while (currentContext.isActive) {
- val event = awaitPointerEventScope { awaitPointerEvent() }
- when (event.type) {
- PointerEventType.Enter -> emitEnter()
- PointerEventType.Exit -> emitExit()
+ awaitPointerEventScope {
+ while (currentContext.isActive) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> scope.launch { emitEnter() }
+ PointerEventType.Exit -> scope.launch { emitExit() }
+ }
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 8b20471..4f0750c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -231,6 +231,24 @@
val itemsCount = itemProvider.itemCount
+ // can be negative if the content padding is larger than the max size from constraints
+ val mainAxisAvailableSize = if (isVertical) {
+ containerConstraints.maxHeight - totalVerticalPadding
+ } else {
+ containerConstraints.maxWidth - totalHorizontalPadding
+ }
+ val visualItemOffset = if (!reverseLayout || mainAxisAvailableSize > 0) {
+ IntOffset(startPadding, topPadding)
+ } else {
+ // When layout is reversed and paddings together take >100% of the available space,
+ // layout size is coerced to 0 when positioning. To take that space into account,
+ // we offset start padding by negative space between paddings.
+ IntOffset(
+ if (isVertical) startPadding else startPadding + mainAxisAvailableSize,
+ if (isVertical) topPadding + mainAxisAvailableSize else topPadding
+ )
+ }
+
val measuredItemProvider = LazyMeasuredItemProvider(
contentConstraints,
isVertical,
@@ -251,20 +269,13 @@
beforeContentPadding = beforeContentPadding,
afterContentPadding = afterContentPadding,
spacing = spacing,
- visualOffset = IntOffset(startPadding, topPadding),
+ visualOffset = visualItemOffset,
key = key,
placementAnimator = placementAnimator
)
}
state.premeasureConstraints = measuredItemProvider.childConstraints
- // can be negative if the content padding is larger than the max size from constraints
- val mainAxisAvailableSize = if (isVertical) {
- containerConstraints.maxHeight - totalVerticalPadding
- } else {
- containerConstraints.maxWidth - totalHorizontalPadding
- }
-
val firstVisibleItemIndex: DataIndex
val firstVisibleScrollOffset: Int
Snapshot.withoutReadObservation {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
index c7989d0..448fe9d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
@@ -231,6 +231,24 @@
val itemsCount = itemProvider.itemCount
+ // can be negative if the content padding is larger than the max size from constraints
+ val mainAxisAvailableSize = if (isVertical) {
+ constraints.maxHeight - totalVerticalPadding
+ } else {
+ constraints.maxWidth - totalHorizontalPadding
+ }
+ val visualItemOffset = if (!reverseLayout || mainAxisAvailableSize > 0) {
+ IntOffset(startPadding, topPadding)
+ } else {
+ // When layout is reversed and paddings together take >100% of the available space,
+ // layout size is coerced to 0 when positioning. To take that space into account,
+ // we offset start padding by negative space between paddings.
+ IntOffset(
+ if (isVertical) startPadding else startPadding + mainAxisAvailableSize,
+ if (isVertical) topPadding + mainAxisAvailableSize else topPadding
+ )
+ }
+
val measuredItemProvider = LazyMeasuredItemProvider(
itemProvider,
this,
@@ -246,7 +264,7 @@
layoutDirection = layoutDirection,
beforeContentPadding = beforeContentPadding,
afterContentPadding = afterContentPadding,
- visualOffset = IntOffset(startPadding, topPadding),
+ visualOffset = visualItemOffset,
placeables = placeables,
placementAnimator = placementAnimator
)
@@ -285,13 +303,6 @@
result
}
- // can be negative if the content padding is larger than the max size from constraints
- val mainAxisAvailableSize = if (isVertical) {
- constraints.maxHeight - totalVerticalPadding
- } else {
- constraints.maxWidth - totalHorizontalPadding
- }
-
val firstVisibleLineIndex: LineIndex
val firstVisibleLineScrollOffset: Int
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
index ddf3d1f..5ed26fc 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
@@ -38,6 +38,7 @@
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.semantics
@@ -62,6 +63,7 @@
import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.height
import androidx.compose.ui.unit.width
@@ -343,6 +345,29 @@
assertThat(textStyle).isEqualTo(expectedTextStyle)
}
+ @Test
+ fun centerAlignedTopAppBar_measureWithNonZeroMinWidth() {
+ var appBarSize = IntSize.Zero
+ rule.setMaterialContent(lightColorScheme()) {
+ CenterAlignedTopAppBar(
+ modifier = Modifier.layout { measurable, constraints ->
+ val placeable = measurable.measure(
+ constraints.copy(minWidth = constraints.maxWidth)
+ )
+ appBarSize = IntSize(placeable.width, placeable.height)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ },
+ title = {
+ Text("Title")
+ }
+ )
+ }
+
+ assertThat(appBarSize).isNotEqualTo(IntSize.Zero)
+ }
+
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Test
fun centerAlignedTopAppBar_contentColor() {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
index fd41db4..afab12b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
@@ -66,6 +66,7 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
@@ -1002,16 +1003,22 @@
modifier = modifier
) { measurables, constraints ->
val navigationIconPlaceable =
- measurables.first { it.layoutId == "navigationIcon" }.measure(constraints)
+ measurables.first { it.layoutId == "navigationIcon" }
+ .measure(constraints.copy(minWidth = 0))
val actionIconsPlaceable =
- measurables.first { it.layoutId == "actionIcons" }.measure(constraints)
+ measurables.first { it.layoutId == "actionIcons" }
+ .measure(constraints.copy(minWidth = 0))
- val maxTitleWidth =
- constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width
+ val maxTitleWidth = if (constraints.maxWidth == Constraints.Infinity) {
+ constraints.maxWidth
+ } else {
+ (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
+ .coerceAtLeast(0)
+ }
val titlePlaceable =
- measurables
- .first { it.layoutId == "title" }
- .measure(constraints.copy(maxWidth = maxTitleWidth))
+ measurables.first { it.layoutId == "title" }
+ .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth))
+
// Locate the title's baseline.
val titleBaseline =
if (titlePlaceable[LastBaseline] != AlignmentLine.Unspecified) {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index fcb4815..8ce5bfd 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -1027,9 +1027,10 @@
/**
* The result of a applying a mutable snapshot. [Success] indicates that the snapshot was
- * successfully applied and is not visible as the global state of the state object (or visible
+ * successfully applied and is now visible as the global state of the state object (or visible
* in the parent snapshot for a nested snapshot). [Failure] indicates one or more state objects
- * were modified by both this snapshot and in the global (or parent) snapshot.
+ * were modified by both this snapshot and in the global (or parent) snapshot, and the changes from
+ * this snapshot are **not** visible in the global or parent snapshot.
*/
sealed class SnapshotApplyResult {
/**
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
index 2d5bf83..780227e 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
@@ -17,46 +17,22 @@
package androidx.compose.ui.input.nestedscroll
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.background
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.CoordinatesProvider
-import androidx.test.espresso.action.GeneralLocation
-import androidx.test.espresso.action.GeneralSwipeAction
-import androidx.test.espresso.action.Press
-import androidx.test.espresso.action.Swipe
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import kotlin.math.abs
-import kotlin.math.sign
import kotlinx.coroutines.isActive
import kotlinx.coroutines.runBlocking
-import org.hamcrest.CoreMatchers.allOf
-import org.hamcrest.CoreMatchers.instanceOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -985,133 +961,7 @@
}
}
- @Test
- fun nestedScroll_movingTarget_velocityShouldRespectSign() {
- var lastVelocity = Velocity.Zero
- val MaxOffsetBound = 900f
- val ConsumedEverything = 0.0f
- rule.setContent {
- var offset by remember {
- mutableStateOf(MaxOffsetBound)
- }
- val nestedScrollConnection = remember {
- object : NestedScrollConnection {
- fun consumedDelta(scrollDelta: Float): Float {
- if (offset == 0f && scrollDelta < 0f) return ConsumedEverything
- if (offset == MaxOffsetBound && scrollDelta > 0f) return ConsumedEverything
- val previousOffset = offset
- offset = (scrollDelta + offset).coerceIn(0f, MaxOffsetBound)
- return offset - previousOffset
- }
-
- override fun onPreScroll(
- available: Offset,
- source: NestedScrollSource
- ): Offset {
- return if (available.y < 0) {
- val consumed = consumedDelta(available.y)
- Offset(x = 0f, y = consumed)
- } else Offset.Zero
- }
-
- override fun onPostScroll(
- consumed: Offset,
- available: Offset,
- source: NestedScrollSource
- ): Offset {
- return if (abs(available.y) > 0f &&
- available.y > 0f
- ) {
- Offset(0f, consumedDelta(available.y))
- } else
- super.onPostScroll(consumed, available, source)
- }
-
- override suspend fun onPreFling(available: Velocity): Velocity {
- lastVelocity = available
- return super.onPreFling(available)
- }
- }
- }
-
- Column(modifier = Modifier.fillMaxSize()) {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .height(80.dp)
- )
- LazyColumn(
- modifier = Modifier
- .graphicsLayer {
- translationY = offset
- }
- .nestedScroll(connection = nestedScrollConnection)
- .fillMaxWidth()
- .weight(1f)
- ) {
- items(100) {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .height(60.dp)
- .background(Color.Gray)
- ) {
- BasicText(text = it.toString())
- }
- Spacer(modifier = Modifier.height(8.dp))
- }
- }
- }
- }
-
- composeViewSwipeUp()
- rule.runOnIdle {
- // swipe ups provide negative signed velocities
- assertThat(sign(lastVelocity.y)).isEqualTo(-1)
- }
- composeViewSwipeDown()
- rule.runOnIdle {
- // swipe downs provide positive signed velocities
- assertThat(sign(lastVelocity.y)).isEqualTo(1)
- }
- composeViewSwipeDown()
- rule.runOnIdle {
- // swipe downs provide positive signed velocities
- assertThat(sign(lastVelocity.y)).isEqualTo(1)
- }
- }
-
-// helper functions
-
- private fun composeViewSwipeUp() {
- onView(allOf(instanceOf(AbstractComposeView::class.java)))
- .perform(
- espressoSwipe(
- GeneralLocation.BOTTOM_CENTER,
- GeneralLocation.CENTER
- )
- )
- }
-
- private fun composeViewSwipeDown() {
- onView(allOf(instanceOf(AbstractComposeView::class.java)))
- .perform(
- espressoSwipe(
- GeneralLocation.CENTER,
- GeneralLocation.BOTTOM_CENTER
- )
- )
- }
-
- private fun espressoSwipe(
- start: CoordinatesProvider,
- end: CoordinatesProvider
- ): GeneralSwipeAction {
- return GeneralSwipeAction(
- Swipe.FAST, start, end,
- Press.FINGER
- )
- }
+ // helper functions
private fun testMiddleParentAdditionRemoval(
content: @Composable (root: Modifier, middle: Modifier, child: Modifier) -> Unit
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
index b6d3da5..580a3b8d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
@@ -167,7 +167,6 @@
private val samples: Array<PointAtTime?> = Array(HistorySize) { null }
private var index: Int = 0
private val useImpulse = true
- internal var currentPositionDeltaAccumulator = Offset.Zero
/**
* Adds a position as the given time to the tracker.
@@ -246,7 +245,6 @@
*/
fun resetTracking() {
samples.fill(element = null)
- currentPositionDeltaAccumulator = Offset.Zero
}
/**
@@ -341,13 +339,9 @@
fun VelocityTracker.addPointerInputChange(event: PointerInputChange) {
@OptIn(ExperimentalComposeUiApi::class)
event.historical.fastForEach {
- val historicalDelta = event.position - it.position
- currentPositionDeltaAccumulator += historicalDelta
- addPosition(it.uptimeMillis, currentPositionDeltaAccumulator)
+ addPosition(it.uptimeMillis, it.position)
}
- val delta = event.position - event.previousPosition
- currentPositionDeltaAccumulator += delta
- addPosition(event.uptimeMillis, currentPositionDeltaAccumulator)
+ addPosition(event.uptimeMillis, event.position)
}
private data class PointAtTime(val point: Offset, val time: Long)
@@ -355,7 +349,7 @@
/**
* A two dimensional velocity estimate.
*
- * VelocityEstimates are computed by [VelocityTracker.getLsq2VelocityEstimate]. An
+ * VelocityEstimates are computed by [VelocityTracker.getVelocityEstimate]. An
* estimate's [confidence] measures how well the velocity tracker's position
* data fit a straight line, [durationMillis] is the time that elapsed between the
* first and last position sample used to compute the velocity, and [offset]
diff --git a/core/core-i18n/build.gradle b/core/core-i18n/build.gradle
index 9af09f9..970de8e 100644
--- a/core/core-i18n/build.gradle
+++ b/core/core-i18n/build.gradle
@@ -30,6 +30,7 @@
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.testRunner)
+ androidTestImplementation(project(":core:core"))
}
androidx {
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
index 6ded3bb..acacedd 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
@@ -18,6 +18,7 @@
import android.os.Build
import android.util.Log
+import androidx.core.os.BuildCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
@@ -46,9 +47,11 @@
private const val AVAILABLE_PERIOD_B = Build.VERSION_CODES.Q
}
- // To make the if (version > ?) a bit more readable
+ /** Starting with Android N ICU4J is public API. */
private val isIcuAvailable = Build.VERSION.SDK_INT >= AVAILABLE_ICU4J
+ /** Starting with Android S ICU honors the "-u-hc-" extension in locale id. */
private val isHcExtensionHonored = Build.VERSION.SDK_INT >= AVAILABLE_HC_U_EXT
+ /** Starting with Android Q ICU supports "b" and "B". */
private val isFlexiblePeriodAvailable = Build.VERSION.SDK_INT >= AVAILABLE_PERIOD_B
private val logTag = this::class.qualifiedName
@@ -279,7 +282,10 @@
} else {
"12:43 AM || 4:43 AM || 8:43 AM || 12:43 PM || 4:43 PM || 8:43 PM"
}
- val expectedZh = if (isFlexiblePeriodAvailable) {
+ val expectedZh = if (BuildCompat.isAtLeastT()) {
+ // Chinese changed to 24h from ICU 70.1
+ "00:43 || 04:43 || 08:43 || 12:43 || 16:43 || 20:43"
+ } else if (isFlexiblePeriodAvailable) {
"凌晨12:43 || 凌晨4:43 || 上午8:43 || 中午12:43 || 下午4:43 || 晚上8:43"
} else {
"上午12:43 || 上午4:43 || 上午8:43 || 下午12:43 || 下午4:43 || 下午8:43"
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index d09a27d..684c9866 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -643,4 +643,5 @@
Setting the namespace via a source AndroidManifest\.xml's package attribute is deprecated\.
Please instead set the namespace \(or testNamespace\) in the module's build\.gradle file, as described here: https://developer\.android\.com/studio/build/configure\-app\-module\#set\-namespace
This migration can be done automatically using the AGP Upgrade Assistant, please refer to https://developer\.android\.com/studio/build/agp\-upgrade\-assistant for more information\.
-
+# > Task :room:integration-tests:room-testapp:mergeDexWithExpandProjectionDebugAndroidTest b/233674378
+WARNING:D8: Application does not contain `androidx\.tracing\.Trace` as referenced in main-dex-list\.
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index ab2be5a..99cfc43 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -36,6 +36,7 @@
docs(project(":browser:browser"))
docs(project(":camera:camera-camera2"))
docs(project(":camera:camera-core"))
+ docs(project(":camera:camera-effects"))
docs(project(":camera:camera-extensions"))
stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
docs(project(":camera:camera-mlkit-vision"))
diff --git a/media2/OWNERS b/media2/OWNERS
index afe4bcd..e629564 100644
--- a/media2/OWNERS
+++ b/media2/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 461042
+# Bug component: 188488
andrewlewis@google.com
gyumin@google.com
hdmoon@google.com
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 76b7067..de61639 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -40,6 +40,13 @@
buildFeatures {
aidl = true
}
+ buildTypes {
+ debug {
+ // Need to make sure androidx.tracing.Trace gets put in primary dex for legacy multidex
+ // as it is needed by androidx.test.runner.AndroidJUnitRunner
+ multiDexKeepFile file('multidex-config.txt')
+ }
+ }
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
androidTest.assets.srcDirs += files("$projectDir/databases".toString())
diff --git a/room/integration-tests/testapp/multidex-config.txt b/room/integration-tests/testapp/multidex-config.txt
new file mode 100644
index 0000000..c108ccb
--- /dev/null
+++ b/room/integration-tests/testapp/multidex-config.txt
@@ -0,0 +1 @@
+androidx/tracing/Trace.class
diff --git a/settings.gradle b/settings.gradle
index e648143..363b8e6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -351,6 +351,7 @@
includeProject(":camera:camera-camera2-pipe-integration", [BuildType.MAIN])
includeProject(":camera:camera-camera2-pipe-testing", [BuildType.MAIN])
includeProject(":camera:camera-core", [BuildType.MAIN])
+includeProject(":camera:camera-effects", [BuildType.MAIN])
includeProject(":camera:camera-extensions", [BuildType.MAIN])
includeProject(":camera:camera-extensions-stub", [BuildType.MAIN])
includeProject(":camera:camera-lifecycle", [BuildType.MAIN])
@@ -361,6 +362,7 @@
includeProject(":camera:camera-view", [BuildType.MAIN])
includeProject(":camera:integration-tests:camera-testapp-camera2-pipe", "camera/integration-tests/camerapipetestapp", [BuildType.MAIN])
includeProject(":camera:integration-tests:camera-testapp-core", "camera/integration-tests/coretestapp", [BuildType.MAIN])
+includeProject(":camera:integration-tests:camera-testapp-diagnose", "camera/integration-tests/diagnosetestapp", [BuildType.MAIN])
includeProject(":camera:integration-tests:camera-testapp-extensions", "camera/integration-tests/extensionstestapp", [BuildType.MAIN])
includeProject(":camera:integration-tests:camera-testapp-viewfinder", "camera/integration-tests/viewfindertestapp", [BuildType.MAIN])
includeProject(":camera:integration-tests:camera-testapp-timing", "camera/integration-tests/timingtestapp", [BuildType.MAIN])
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
index 2d1f45e..5238b46 100644
--- a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
@@ -48,7 +48,7 @@
val temporaryFolder = TemporaryFolder(getInstrumentation().context.cacheDir)
@Test
- @FlakyTest(bugId = 159202455)
+ @Ignore // b/159202455
fun test_exec_hook_methods() = test_simple_hook_methods(
listOf(
"execute()V",
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d3855f7..de42dca 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -471,7 +471,10 @@
*
* If an androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition metadata tag is defined
* for your WatchFaceService 's manifest, and your XML includes <ComplicationSlot> tags then you
- * must override this method.
+ * must override this method. An exception will be thrown if the implementation returns null.
+ *
+ * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+ * [UserStyleSchema] returned by [createUserStyleSchema].
*/
@Suppress("DEPRECATION")
@WorkerThread