Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 1c47c3b

Browse files
authored
[camera] Add ProcessCameraProvider class to CameraX plugin (#6469)
1 parent 2592df9 commit 1c47c3b

15 files changed

+818
-7
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
* Creates camera_android_camerax plugin for development.
44
* Adds CameraInfo class and removes unnecessary code from plugin.
55
* Adds CameraSelector class.
6+
* Adds ProcessCameraProvider class.

packages/camera/camera_android_camerax/android/build.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ android {
3838

3939
dependencies {
4040
// CameraX core library using the camera2 implementation must use same version number.
41-
def camerax_version = "1.2.0-beta01"
41+
def camerax_version = "1.2.0-beta02"
4242
implementation "androidx.camera:camera-core:${camerax_version}"
4343
implementation "androidx.camera:camera-camera2:${camerax_version}"
4444
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
45+
implementation 'com.google.guava:guava:28.1-android'
4546
testImplementation 'junit:junit:4.13.2'
4647
testImplementation 'org.mockito:mockito-inline:4.7.0'
4748
testImplementation 'androidx.test:core:1.4.0'
49+
testImplementation 'org.robolectric:robolectric:4.3'
4850
}
4951
testOptions {
5052
unitTests.includeAndroidResources = true

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java

+27-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
1616
private InstanceManager instanceManager;
1717
private FlutterPluginBinding pluginBinding;
18+
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
1819

1920
/**
2021
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
@@ -39,6 +40,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
3940
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
4041
GeneratedCameraXLibrary.CameraSelectorHostApi.setup(
4142
binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager));
43+
processCameraProviderHostApi =
44+
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
45+
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
46+
binaryMessenger, processCameraProviderHostApi);
4247
}
4348

4449
@Override
@@ -60,15 +65,33 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
6065
// Activity Lifecycle methods:
6166

6267
@Override
63-
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {}
68+
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
69+
updateContext(activityPluginBinding.getActivity());
70+
}
6471

6572
@Override
66-
public void onDetachedFromActivityForConfigChanges() {}
73+
public void onDetachedFromActivityForConfigChanges() {
74+
updateContext(pluginBinding.getApplicationContext());
75+
}
6776

6877
@Override
6978
public void onReattachedToActivityForConfigChanges(
70-
@NonNull ActivityPluginBinding activityPluginBinding) {}
79+
@NonNull ActivityPluginBinding activityPluginBinding) {
80+
updateContext(activityPluginBinding.getActivity());
81+
}
7182

7283
@Override
73-
public void onDetachedFromActivity() {}
84+
public void onDetachedFromActivity() {
85+
updateContext(pluginBinding.getApplicationContext());
86+
}
87+
88+
/**
89+
* Updates context that is used to fetch the corresponding instance of a {@code
90+
* ProcessCameraProvider}.
91+
*/
92+
private void updateContext(Context context) {
93+
if (processCameraProviderHostApi != null) {
94+
processCameraProviderHostApi.setContext(context);
95+
}
96+
}
7497
}

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ public CameraInfoFlutterApiImpl(
1818
}
1919

2020
void create(CameraInfo cameraInfo, Reply<Void> reply) {
21-
instanceManager.addHostCreatedInstance(cameraInfo);
22-
create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply);
21+
create(instanceManager.addHostCreatedInstance(cameraInfo), reply);
2322
}
2423
}

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java

+134
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
/** Generated class from Pigeon. */
2323
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
2424
public class GeneratedCameraXLibrary {
25+
26+
public interface Result<T> {
27+
void success(T result);
28+
29+
void error(Throwable error);
30+
}
31+
2532
private static class JavaObjectHostApiCodec extends StandardMessageCodec {
2633
public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec();
2734

@@ -311,6 +318,133 @@ public void create(
311318
}
312319
}
313320

321+
private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec {
322+
public static final ProcessCameraProviderHostApiCodec INSTANCE =
323+
new ProcessCameraProviderHostApiCodec();
324+
325+
private ProcessCameraProviderHostApiCodec() {}
326+
}
327+
328+
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
329+
public interface ProcessCameraProviderHostApi {
330+
void getInstance(Result<Long> result);
331+
332+
@NonNull
333+
List<Long> getAvailableCameraInfos(@NonNull Long identifier);
334+
335+
/** The codec used by ProcessCameraProviderHostApi. */
336+
static MessageCodec<Object> getCodec() {
337+
return ProcessCameraProviderHostApiCodec.INSTANCE;
338+
}
339+
340+
/**
341+
* Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the
342+
* `binaryMessenger`.
343+
*/
344+
static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) {
345+
{
346+
BasicMessageChannel<Object> channel =
347+
new BasicMessageChannel<>(
348+
binaryMessenger,
349+
"dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance",
350+
getCodec());
351+
if (api != null) {
352+
channel.setMessageHandler(
353+
(message, reply) -> {
354+
Map<String, Object> wrapped = new HashMap<>();
355+
try {
356+
Result<Long> resultCallback =
357+
new Result<Long>() {
358+
public void success(Long result) {
359+
wrapped.put("result", result);
360+
reply.reply(wrapped);
361+
}
362+
363+
public void error(Throwable error) {
364+
wrapped.put("error", wrapError(error));
365+
reply.reply(wrapped);
366+
}
367+
};
368+
369+
api.getInstance(resultCallback);
370+
} catch (Error | RuntimeException exception) {
371+
wrapped.put("error", wrapError(exception));
372+
reply.reply(wrapped);
373+
}
374+
});
375+
} else {
376+
channel.setMessageHandler(null);
377+
}
378+
}
379+
{
380+
BasicMessageChannel<Object> channel =
381+
new BasicMessageChannel<>(
382+
binaryMessenger,
383+
"dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos",
384+
getCodec());
385+
if (api != null) {
386+
channel.setMessageHandler(
387+
(message, reply) -> {
388+
Map<String, Object> wrapped = new HashMap<>();
389+
try {
390+
ArrayList<Object> args = (ArrayList<Object>) message;
391+
Number identifierArg = (Number) args.get(0);
392+
if (identifierArg == null) {
393+
throw new NullPointerException("identifierArg unexpectedly null.");
394+
}
395+
List<Long> output =
396+
api.getAvailableCameraInfos(
397+
(identifierArg == null) ? null : identifierArg.longValue());
398+
wrapped.put("result", output);
399+
} catch (Error | RuntimeException exception) {
400+
wrapped.put("error", wrapError(exception));
401+
}
402+
reply.reply(wrapped);
403+
});
404+
} else {
405+
channel.setMessageHandler(null);
406+
}
407+
}
408+
}
409+
}
410+
411+
private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec {
412+
public static final ProcessCameraProviderFlutterApiCodec INSTANCE =
413+
new ProcessCameraProviderFlutterApiCodec();
414+
415+
private ProcessCameraProviderFlutterApiCodec() {}
416+
}
417+
418+
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
419+
public static class ProcessCameraProviderFlutterApi {
420+
private final BinaryMessenger binaryMessenger;
421+
422+
public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) {
423+
this.binaryMessenger = argBinaryMessenger;
424+
}
425+
426+
public interface Reply<T> {
427+
void reply(T reply);
428+
}
429+
430+
static MessageCodec<Object> getCodec() {
431+
return ProcessCameraProviderFlutterApiCodec.INSTANCE;
432+
}
433+
434+
public void create(@NonNull Long identifierArg, Reply<Void> callback) {
435+
BasicMessageChannel<Object> channel =
436+
new BasicMessageChannel<>(
437+
binaryMessenger,
438+
"dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create",
439+
getCodec());
440+
channel.send(
441+
new ArrayList<Object>(Arrays.asList(identifierArg)),
442+
channelReply -> {
443+
callback.reply(null);
444+
});
445+
}
446+
}
447+
314448
private static Map<String, Object> wrapError(Throwable exception) {
315449
Map<String, Object> errorMap = new HashMap<>();
316450
errorMap.put("message", exception.toString());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import androidx.camera.lifecycle.ProcessCameraProvider;
8+
import io.flutter.plugin.common.BinaryMessenger;
9+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderFlutterApi;
10+
11+
public class ProcessCameraProviderFlutterApiImpl extends ProcessCameraProviderFlutterApi {
12+
public ProcessCameraProviderFlutterApiImpl(
13+
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
14+
super(binaryMessenger);
15+
this.instanceManager = instanceManager;
16+
}
17+
18+
private final InstanceManager instanceManager;
19+
20+
void create(ProcessCameraProvider processCameraProvider, Reply<Void> reply) {
21+
create(instanceManager.addHostCreatedInstance(processCameraProvider), reply);
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import android.content.Context;
8+
import androidx.annotation.NonNull;
9+
import androidx.camera.core.CameraInfo;
10+
import androidx.camera.lifecycle.ProcessCameraProvider;
11+
import androidx.core.content.ContextCompat;
12+
import com.google.common.util.concurrent.ListenableFuture;
13+
import io.flutter.plugin.common.BinaryMessenger;
14+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi;
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
18+
public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi {
19+
private final BinaryMessenger binaryMessenger;
20+
private final InstanceManager instanceManager;
21+
22+
private Context context;
23+
24+
public ProcessCameraProviderHostApiImpl(
25+
BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) {
26+
this.binaryMessenger = binaryMessenger;
27+
this.instanceManager = instanceManager;
28+
this.context = context;
29+
}
30+
31+
/**
32+
* Sets the context that the {@code ProcessCameraProvider} will use to attach the lifecycle of the
33+
* camera to.
34+
*
35+
* <p>If using the camera plugin in an add-to-app context, ensure that a new instance of the
36+
* {@code ProcessCameraProvider} is fetched via {@code #getInstance} anytime the context changes.
37+
*/
38+
public void setContext(Context context) {
39+
this.context = context;
40+
}
41+
42+
/**
43+
* Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the
44+
* current {@code Context}.
45+
*/
46+
@Override
47+
public void getInstance(GeneratedCameraXLibrary.Result<Long> result) {
48+
ListenableFuture<ProcessCameraProvider> processCameraProviderFuture =
49+
ProcessCameraProvider.getInstance(context);
50+
51+
processCameraProviderFuture.addListener(
52+
() -> {
53+
try {
54+
// Camera provider is now guaranteed to be available.
55+
ProcessCameraProvider processCameraProvider = processCameraProviderFuture.get();
56+
57+
if (!instanceManager.containsInstance(processCameraProvider)) {
58+
final ProcessCameraProviderFlutterApiImpl flutterApi =
59+
new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager);
60+
flutterApi.create(processCameraProvider, reply -> {});
61+
}
62+
result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider));
63+
} catch (Exception e) {
64+
result.error(e);
65+
}
66+
},
67+
ContextCompat.getMainExecutor(context));
68+
}
69+
70+
/** Returns cameras available to the ProcessCameraProvider. */
71+
@Override
72+
public List<Long> getAvailableCameraInfos(@NonNull Long identifier) {
73+
ProcessCameraProvider processCameraProvider =
74+
(ProcessCameraProvider) instanceManager.getInstance(identifier);
75+
76+
List<CameraInfo> availableCameras = processCameraProvider.getAvailableCameraInfos();
77+
List<Long> availableCamerasIds = new ArrayList<Long>();
78+
final CameraInfoFlutterApiImpl cameraInfoFlutterApi =
79+
new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);
80+
81+
for (CameraInfo cameraInfo : availableCameras) {
82+
cameraInfoFlutterApi.create(cameraInfo, result -> {});
83+
availableCamerasIds.add(instanceManager.getIdentifierForStrongReference(cameraInfo));
84+
}
85+
return availableCamerasIds;
86+
}
87+
}

0 commit comments

Comments
 (0)