-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[camerax] Wrap Android classes/methods required to set the exposure m…
…ode (#5966) Wraps classes/methods needed to set capture request options that will allow us to implement setting the exposure mode, namely: **Camera2CameraControl** - `create` method using [`from`](https://developer.android.com/reference/androidx/camera/camera2/interop/Camera2CameraControl#from(androidx.camera.core.CameraControl)) - [`setCaptureRequestOptions`](https://developer.android.com/reference/androidx/camera/camera2/interop/Camera2CameraControl#setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions)) **CaptureRequestOptions** - `create` method using its [builder](https://developer.android.com/reference/androidx/camera/camera2/interop/CaptureRequestOptions.Builder)* Part of flutter/flutter#120468. *Note that this required that I specify types of supported capture request options due to the Dart/native split. I took inspiration from our previous implementation of supported `LiveData` types (see [pigeon file](https://github.com/flutter/packages/blob/73c9bdc59b8d51ee558bff113bcdcdb53485cd08/packages/camera/camera_android_camerax/pigeons/camerax_library.dart#L80) for details).
- Loading branch information
Showing
27 changed files
with
1,668 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
...rax/android/src/main/java/io/flutter/plugins/camerax/Camera2CameraControlHostApiImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
package io.flutter.plugins.camerax; | ||
|
||
import android.content.Context; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.OptIn; | ||
import androidx.annotation.VisibleForTesting; | ||
import androidx.camera.camera2.interop.Camera2CameraControl; | ||
import androidx.camera.camera2.interop.CaptureRequestOptions; | ||
import androidx.camera.core.CameraControl; | ||
import androidx.core.content.ContextCompat; | ||
import com.google.common.util.concurrent.FutureCallback; | ||
import com.google.common.util.concurrent.Futures; | ||
import com.google.common.util.concurrent.ListenableFuture; | ||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Camera2CameraControlHostApi; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Host API implementation for {@link Camera2CameraControl}. | ||
* | ||
* <p>This class may handle instantiating and adding native object instances that are attached to a | ||
* Dart instance or handle method calls on the associated native class or an instance of the class. | ||
*/ | ||
public class Camera2CameraControlHostApiImpl implements Camera2CameraControlHostApi { | ||
private final InstanceManager instanceManager; | ||
private final Camera2CameraControlProxy proxy; | ||
|
||
/** Proxy for constructor and methods of {@link Camera2CameraControl}. */ | ||
@VisibleForTesting | ||
public static class Camera2CameraControlProxy { | ||
Context context; | ||
|
||
/** | ||
* Creates an instance of {@link Camera2CameraControl} derived from specified {@link | ||
* CameraControl} instance. | ||
*/ | ||
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class) | ||
public @NonNull Camera2CameraControl create(@NonNull CameraControl cameraControl) { | ||
return Camera2CameraControl.from(cameraControl); | ||
} | ||
|
||
/** | ||
* Adds a {@link CaptureRequestOptions} to update the capture session with the options it | ||
* contains. | ||
*/ | ||
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class) | ||
public void addCaptureRequestOptions( | ||
@NonNull Camera2CameraControl camera2CameraControl, | ||
@NonNull CaptureRequestOptions bundle, | ||
@NonNull GeneratedCameraXLibrary.Result<Void> result) { | ||
if (context == null) { | ||
throw new IllegalStateException("Context must be set to add capture request options."); | ||
} | ||
|
||
ListenableFuture<Void> addCaptureRequestOptionsFuture = | ||
camera2CameraControl.addCaptureRequestOptions(bundle); | ||
|
||
Futures.addCallback( | ||
addCaptureRequestOptionsFuture, | ||
new FutureCallback<Void>() { | ||
public void onSuccess(Void voidResult) { | ||
result.success(null); | ||
} | ||
|
||
public void onFailure(Throwable t) { | ||
result.error(t); | ||
} | ||
}, | ||
ContextCompat.getMainExecutor(context)); | ||
} | ||
} | ||
|
||
/** | ||
* Constructs a {@link Camera2CameraControlHostApiImpl}. | ||
* | ||
* @param instanceManager maintains instances stored to communicate with attached Dart objects | ||
* @param context {@link Context} used to retrieve {@code Executor} | ||
*/ | ||
public Camera2CameraControlHostApiImpl( | ||
@NonNull InstanceManager instanceManager, @NonNull Context context) { | ||
this(instanceManager, new Camera2CameraControlProxy(), context); | ||
} | ||
|
||
/** | ||
* Constructs a {@link Camera2CameraControlHostApiImpl}. | ||
* | ||
* @param instanceManager maintains instances stored to communicate with attached Dart objects | ||
* @param proxy proxy for constructor and methods of {@link Camera2CameraControl} | ||
* @param context {@link Context} used to retrieve {@code Executor} | ||
*/ | ||
@VisibleForTesting | ||
Camera2CameraControlHostApiImpl( | ||
@NonNull InstanceManager instanceManager, | ||
@NonNull Camera2CameraControlProxy proxy, | ||
@NonNull Context context) { | ||
this.instanceManager = instanceManager; | ||
this.proxy = proxy; | ||
proxy.context = context; | ||
} | ||
|
||
/** | ||
* Sets the context that the {@code Camera2CameraControl} will use to listen for the result of | ||
* setting capture request options. | ||
* | ||
* <p>If using the camera plugin in an add-to-app context, ensure that this is called anytime that | ||
* the context changes. | ||
*/ | ||
public void setContext(@NonNull Context context) { | ||
this.proxy.context = context; | ||
} | ||
|
||
@Override | ||
public void create(@NonNull Long identifier, @NonNull Long cameraControlIdentifier) { | ||
instanceManager.addDartCreatedInstance( | ||
proxy.create(Objects.requireNonNull(instanceManager.getInstance(cameraControlIdentifier))), | ||
identifier); | ||
} | ||
|
||
@Override | ||
public void addCaptureRequestOptions( | ||
@NonNull Long identifier, | ||
@NonNull Long captureRequestOptionsIdentifier, | ||
@NonNull GeneratedCameraXLibrary.Result<Void> result) { | ||
proxy.addCaptureRequestOptions( | ||
getCamera2CameraControlInstance(identifier), | ||
Objects.requireNonNull(instanceManager.getInstance(captureRequestOptionsIdentifier)), | ||
result); | ||
} | ||
|
||
/** | ||
* Retrieves the {@link Camera2CameraControl} instance associated with the specified {@code | ||
* identifier}. | ||
*/ | ||
private Camera2CameraControl getCamera2CameraControlInstance(@NonNull Long identifier) { | ||
return Objects.requireNonNull(instanceManager.getInstance(identifier)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
...ax/android/src/main/java/io/flutter/plugins/camerax/CaptureRequestOptionsHostApiImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
package io.flutter.plugins.camerax; | ||
|
||
import android.hardware.camera2.CaptureRequest; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.OptIn; | ||
import androidx.annotation.VisibleForTesting; | ||
import androidx.camera.camera2.interop.CaptureRequestOptions; | ||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CaptureRequestKeySupportedType; | ||
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CaptureRequestOptionsHostApi; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* Host API implementation for {@link CaptureRequestOptions}. | ||
* | ||
* <p>This class may handle instantiating and adding native object instances that are attached to a | ||
* Dart instance or handle method calls on the associated native class or an instance of the class. | ||
*/ | ||
public class CaptureRequestOptionsHostApiImpl implements CaptureRequestOptionsHostApi { | ||
private final InstanceManager instanceManager; | ||
private final CaptureRequestOptionsProxy proxy; | ||
|
||
/** Proxy for constructor of {@link CaptureRequestOptions}. */ | ||
@VisibleForTesting | ||
public static class CaptureRequestOptionsProxy { | ||
/** Creates an instance of {@link CaptureRequestOptions}. */ | ||
// Suppression is safe because the type shared between the key and value pairs that | ||
// represent capture request options is checked on the Dart side. | ||
@SuppressWarnings("unchecked") | ||
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class) | ||
public @NonNull CaptureRequestOptions create( | ||
@NonNull Map<CaptureRequestKeySupportedType, Object> options) { | ||
CaptureRequestOptions.Builder builder = getCaptureRequestOptionsBuilder(); | ||
|
||
for (Map.Entry<CaptureRequestKeySupportedType, Object> option : options.entrySet()) { | ||
CaptureRequestKeySupportedType optionKeyType = option.getKey(); | ||
CaptureRequest.Key<? extends Object> optionKey = getCaptureRequestKey(optionKeyType); | ||
Object optionValue = option.getValue(); | ||
|
||
if (optionValue == null) { | ||
builder.clearCaptureRequestOption(optionKey); | ||
continue; | ||
} | ||
|
||
switch (optionKeyType) { | ||
case CONTROL_AE_LOCK: | ||
builder.setCaptureRequestOption( | ||
(CaptureRequest.Key<Boolean>) optionKey, (Boolean) optionValue); | ||
break; | ||
default: | ||
throw new IllegalArgumentException( | ||
"The capture request key " | ||
+ optionKeyType.toString() | ||
+ "is not currently supported by the plugin."); | ||
} | ||
} | ||
|
||
return builder.build(); | ||
} | ||
|
||
private CaptureRequest.Key<? extends Object> getCaptureRequestKey( | ||
CaptureRequestKeySupportedType type) { | ||
CaptureRequest.Key<? extends Object> key; | ||
switch (type) { | ||
case CONTROL_AE_LOCK: | ||
key = CaptureRequest.CONTROL_AE_LOCK; | ||
break; | ||
default: | ||
throw new IllegalArgumentException( | ||
"The capture request key is not currently supported by the plugin."); | ||
} | ||
return key; | ||
} | ||
|
||
@VisibleForTesting | ||
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class) | ||
public @NonNull CaptureRequestOptions.Builder getCaptureRequestOptionsBuilder() { | ||
return new CaptureRequestOptions.Builder(); | ||
} | ||
} | ||
|
||
/** | ||
* Constructs a {@link CaptureRequestOptionsHostApiImpl}. | ||
* | ||
* @param instanceManager maintains instances stored to communicate with attached Dart objects | ||
*/ | ||
public CaptureRequestOptionsHostApiImpl(@NonNull InstanceManager instanceManager) { | ||
this(instanceManager, new CaptureRequestOptionsProxy()); | ||
} | ||
|
||
/** | ||
* Constructs a {@link CaptureRequestOptionsHostApiImpl}. | ||
* | ||
* @param instanceManager maintains instances stored to communicate with attached Dart objects | ||
* @param proxy proxy for constructor of {@link CaptureRequestOptions} | ||
*/ | ||
@VisibleForTesting | ||
CaptureRequestOptionsHostApiImpl( | ||
@NonNull InstanceManager instanceManager, @NonNull CaptureRequestOptionsProxy proxy) { | ||
this.instanceManager = instanceManager; | ||
this.proxy = proxy; | ||
} | ||
|
||
@Override | ||
public void create(@NonNull Long identifier, @NonNull Map<Long, Object> options) { | ||
Map<CaptureRequestKeySupportedType, Object> decodedOptions = | ||
new HashMap<CaptureRequestKeySupportedType, Object>(); | ||
for (Map.Entry<Long, Object> option : options.entrySet()) { | ||
decodedOptions.put( | ||
CaptureRequestKeySupportedType.values()[option.getKey().intValue()], option.getValue()); | ||
} | ||
instanceManager.addDartCreatedInstance(proxy.create(decodedOptions), identifier); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.