Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
200ac7f
Test 1 changes
camsim99 Jun 3, 2024
8fe3230
Check transformation info
camsim99 Jun 3, 2024
4fb6f74
Pixel fix
camsim99 Jun 3, 2024
b2fbc7d
change sensor orientation impl -- no change
camsim99 Jun 4, 2024
754e0fd
Working state
camsim99 Jun 4, 2024
cf7c4e6
Corrections pt 1
camsim99 Jun 4, 2024
61b9631
Possible landscape tablet fix + working state for pixel, samsung
camsim99 Jun 5, 2024
dfcfe32
Self-review, add tests, bump version
camsim99 Jun 5, 2024
77ebbfa
Clean up plugin code
camsim99 Jun 5, 2024
9e72c49
Fix current tests
camsim99 Jun 5, 2024
ee6dbe9
Add docs
camsim99 Jun 5, 2024
2b1637e
Add most test coverage
camsim99 Jun 6, 2024
0c8b230
Fix tests
camsim99 Jun 6, 2024
c85942f
Final review
camsim99 Jun 6, 2024
673344d
Add landscape fix guess
camsim99 Jun 6, 2024
0de02e8
Update changelog with hopes and dreams
camsim99 Jun 6, 2024
989eea6
Merge remote-tracking branch 'upstream/main' into camx_preview_fix
camsim99 Jun 6, 2024
08fd90d
Rename var and change landscape guess
camsim99 Jun 6, 2024
b70bc89
address review
camsim99 Jun 6, 2024
40793f9
Merge remote-tracking branch 'upstream/main' into camx_preview_fix
camsim99 Jun 25, 2024
876ee43
Remove todos
camsim99 Jun 25, 2024
65554f6
Name change
camsim99 Jul 2, 2024
58c1646
Merge remote-tracking branch 'upstream/main' into camx_preview_fix
camsim99 Jul 2, 2024
5e72187
Merge remote-tracking branch 'upstream/main' into camx_preview_fix
camsim99 Aug 13, 2024
5c510a7
Merge remote-tracking branch 'upstream/main' into camx_preview_fix
camsim99 Aug 13, 2024
c91984e
Add surface producer callback
camsim99 Aug 16, 2024
a05fda8
version bump
camsim99 Aug 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.6.6

* Adds logic to support building a camera preview with Android `Surface`s not backed by a `SurfaceTexture`
to which CameraX cannot not automatically apply the transformation required to achieve the correct rotation.
* Re-lands support for Impeller.
* Updates example app's minimum supported SDK version to Flutter 3.22/Dart 3.4.

## 0.6.5+5

* Reverts changes to support Impeller.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,30 @@ public class Camera2CameraInfoHostApiImpl implements Camera2CameraInfoHostApi {

/** Proxy for methods of {@link Camera2CameraInfo}. */
@VisibleForTesting
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public static class Camera2CameraInfoProxy {

@NonNull
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public Camera2CameraInfo createFrom(@NonNull CameraInfo cameraInfo) {
return Camera2CameraInfo.from(cameraInfo);
}

@NonNull
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public Integer getSupportedHardwareLevel(@NonNull Camera2CameraInfo camera2CameraInfo) {
return camera2CameraInfo.getCameraCharacteristic(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
}

@NonNull
@OptIn(markerClass = ExperimentalCamera2Interop.class)
public String getCameraId(@NonNull Camera2CameraInfo camera2CameraInfo) {
return camera2CameraInfo.getCameraId();
}

@NonNull
public Long getSensorOrientation(@NonNull Camera2CameraInfo camera2CameraInfo) {
return Long.valueOf(
camera2CameraInfo.getCameraCharacteristic(CameraCharacteristics.SENSOR_ORIENTATION));
}
}

/**
Expand Down Expand Up @@ -105,6 +109,12 @@ public String getCameraId(@NonNull Long identifier) {
return proxy.getCameraId(getCamera2CameraInfoInstance(identifier));
}

@Override
@NonNull
public Long getSensorOrientation(@NonNull Long identifier) {
return proxy.getSensorOrientation(getCamera2CameraInfoInstance(identifier));
}

private Camera2CameraInfo getCamera2CameraInfoInstance(@NonNull Long identifier) {
return Objects.requireNonNull(instanceManager.getInstance(identifier));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
package io.flutter.plugins.camerax;

import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
Expand Down Expand Up @@ -51,11 +49,6 @@ public class CameraXProxy {
return new Preview.Builder();
}

/** Creates a {@link Surface} instance from the specified {@link SurfaceTexture}. */
public @NonNull Surface createSurface(@NonNull SurfaceTexture surfaceTexture) {
return new Surface(surfaceTexture);
}

/**
* Creates an instance of the {@link SystemServicesFlutterApiImpl}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ interface DeviceOrientationChangeCallback {
* Starts listening to the device's sensors or UI for orientation updates.
*
* <p>When orientation information is updated, the callback method of the {@link
* DeviceOrientationChangeCallback} is called with the new orientation. This latest value can also
* be retrieved through the {@link #getVideoOrientation()} accessor.
* DeviceOrientationChangeCallback} is called with the new orientation.
*
* <p>If the device's ACCELEROMETER_ROTATION setting is enabled the {@link
* DeviceOrientationManager} will report orientation updates based on the sensor information. If
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public void stopListeningForDeviceOrientationChange() {
* for instance for more information on how this default value is used.
*/
@Override
public @NonNull Long getDefaultDisplayRotation() {
@NonNull
public Long getDefaultDisplayRotation() {
int defaultRotation;
try {
defaultRotation = deviceOrientationManager.getDefaultRotation();
Expand All @@ -106,4 +107,11 @@ public void stopListeningForDeviceOrientationChange() {

return Long.valueOf(defaultRotation);
}

/** Gets current UI orientation based on the current device orientation and rotation. */
@Override
@NonNull
public String getUiOrientation() {
return serializeDeviceOrientation(deviceOrientationManager.getUIOrientation());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,9 @@ void requestCameraPermissions(
@NonNull
String getTempFilePath(@NonNull String prefix, @NonNull String suffix);

@NonNull
Boolean isUsingSurfaceTextureForPreview();

/** The codec used by SystemServicesHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return SystemServicesHostApiCodec.INSTANCE;
Expand Down Expand Up @@ -1508,6 +1511,29 @@ public void error(Throwable error) {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.SystemServicesHostApi.isUsingSurfaceTextureForPreview",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
try {
Boolean output = api.isUsingSurfaceTextureForPreview();
wrapped.add(0, output);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
Expand Down Expand Up @@ -1550,6 +1576,9 @@ void startListeningForDeviceOrientationChange(
@NonNull
Long getDefaultDisplayRotation();

@NonNull
String getUiOrientation();

/** The codec used by DeviceOrientationManagerHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
Expand Down Expand Up @@ -1634,6 +1663,29 @@ static void setup(
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.DeviceOrientationManagerHostApi.getUiOrientation",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
try {
String output = api.getUiOrientation();
wrapped.add(0, output);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
Expand Down Expand Up @@ -4389,6 +4441,9 @@ public interface Camera2CameraInfoHostApi {
@NonNull
String getCameraId(@NonNull Long identifier);

@NonNull
Long getSensorOrientation(@NonNull Long identifier);

/** The codec used by Camera2CameraInfoHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
Expand Down Expand Up @@ -4481,6 +4536,33 @@ static void setup(
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.Camera2CameraInfoHostApi.getSensorOrientation",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
try {
Long output =
api.getSensorOrientation(
(identifierArg == null) ? null : identifierArg.longValue());
wrapped.add(0, output);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package io.flutter.plugins.camerax;

import android.graphics.SurfaceTexture;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
Expand All @@ -25,7 +24,7 @@ public class PreviewHostApiImpl implements PreviewHostApi {
private final TextureRegistry textureRegistry;

@VisibleForTesting public @NonNull CameraXProxy cameraXProxy = new CameraXProxy();
@VisibleForTesting public @Nullable TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture;
@VisibleForTesting public @Nullable TextureRegistry.SurfaceProducer flutterSurfaceProducer;

public PreviewHostApiImpl(
@NonNull BinaryMessenger binaryMessenger,
Expand Down Expand Up @@ -62,12 +61,11 @@ public void create(
@Override
public @NonNull Long setSurfaceProvider(@NonNull Long identifier) {
Preview preview = getPreviewInstance(identifier);
flutterSurfaceTexture = textureRegistry.createSurfaceTexture();
SurfaceTexture surfaceTexture = flutterSurfaceTexture.surfaceTexture();
Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(surfaceTexture);
flutterSurfaceProducer = textureRegistry.createSurfaceProducer();
Preview.SurfaceProvider surfaceProvider = createSurfaceProvider(flutterSurfaceProducer);
preview.setSurfaceProvider(surfaceProvider);

return flutterSurfaceTexture.id();
return flutterSurfaceProducer.id();
}

/**
Expand All @@ -76,13 +74,13 @@ public void create(
*/
@VisibleForTesting
public @NonNull Preview.SurfaceProvider createSurfaceProvider(
@NonNull SurfaceTexture surfaceTexture) {
@NonNull TextureRegistry.SurfaceProducer surfaceProducer) {
return new Preview.SurfaceProvider() {
@Override
public void onSurfaceRequested(@NonNull SurfaceRequest request) {
surfaceTexture.setDefaultBufferSize(
surfaceProducer.setSize(
request.getResolution().getWidth(), request.getResolution().getHeight());
Surface flutterSurface = cameraXProxy.createSurface(surfaceTexture);
Surface flutterSurface = surfaceProducer.getSurface();
request.provideSurface(
flutterSurface,
Executors.newSingleThreadExecutor(),
Expand Down Expand Up @@ -133,8 +131,8 @@ String getProvideSurfaceErrorDescription(int resultCode) {
*/
@Override
public void releaseFlutterSurfaceTexture() {
if (flutterSurfaceTexture != null) {
flutterSurfaceTexture.release();
if (flutterSurfaceProducer != null) {
flutterSurfaceProducer.release();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
Expand Down Expand Up @@ -103,4 +104,17 @@ public String getTempFilePath(@NonNull String prefix, @NonNull String suffix) {
null);
}
}

/**
* Returns whether or not a {@code SurfaceTexture} backs the {@code Surface} provided to CameraX
* to build the camera preview.
*
* <p>This is determined by the engine, who uses {@code SurfaceTexture}s on Android SDKs 29 and
* below.
*/
@Override
@NonNull
public Boolean isUsingSurfaceTextureForPreview() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My 2c, and I'd love the thoughts by the other reviewers as well, as I could be wrong - I'd wish for this to say something about the property of the preview, instead of the technology. For example, isPreviewTexturePreRotated (or whatever best describes the situation).

If we agree, this change made elsewhere in the PR as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open to making this change, just let me know!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the change, there is no reason to expose the implementation details here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to isPreviewPreTransformed

return Build.VERSION.SDK_INT <= 29;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,16 @@ public void getDefaultDisplayRotation_returnsExpectedRotation() {

assertEquals(hostApi.getDefaultDisplayRotation(), Long.valueOf(defaultRotation));
}

@Test
public void getUiOrientation_returnsExpectedOrientation() {
final DeviceOrientationManagerHostApiImpl hostApi =
new DeviceOrientationManagerHostApiImpl(mockBinaryMessenger, mockInstanceManager);
final DeviceOrientation uiOrientation = DeviceOrientation.LANDSCAPE_LEFT;

hostApi.deviceOrientationManager = mockDeviceOrientationManager;
when(mockDeviceOrientationManager.getUIOrientation()).thenReturn(uiOrientation);

assertEquals(hostApi.getUiOrientation(), uiOrientation.toString());
}
}
Loading