Skip to content

Commit e9406bc

Browse files
[camerax] Adds functionality to bind UseCases to a lifecycle (flutter#6939)
* Copy over code from proof of concept * Add dart tests * Fix dart tests * Add java tests * Add me as owner and changelog change * Fix analyzer * Add instance manager fix * Update comment * Undo instance manager changes * Formatting * Fix analyze * Address review * Fix analyze * Add import * Fix assertion error * Remove unecessary this keywrod * Update packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java Co-authored-by: Maurice Parrish <[email protected]> Co-authored-by: Maurice Parrish <[email protected]>
1 parent dc8ad77 commit e9406bc

22 files changed

+945
-44
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ packages/**/*_web/** @ditman
2828

2929
# - Android
3030
packages/camera/camera_android/** @camsim99
31+
packages/camera/camera_android_camerax/** @camsim99
3132
packages/espresso/** @GaryQian
3233
packages/flutter_plugin_android_lifecycle/** @GaryQian
3334
packages/google_maps_flutter/google_maps_flutter_android/** @GaryQian

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
* Adds CameraSelector class.
66
* Adds ProcessCameraProvider class.
77
* Bump CameraX version to 1.3.0-alpha02.
8+
* Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider.

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import android.content.Context;
88
import androidx.annotation.NonNull;
9+
import androidx.lifecycle.LifecycleOwner;
910
import io.flutter.embedding.engine.plugins.FlutterPlugin;
1011
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
1112
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
@@ -15,7 +16,7 @@
1516
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
1617
private InstanceManager instanceManager;
1718
private FlutterPluginBinding pluginBinding;
18-
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
19+
public ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
1920

2021
/**
2122
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
@@ -36,10 +37,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
3637
// Set up Host APIs.
3738
GeneratedCameraXLibrary.CameraInfoHostApi.setup(
3839
binaryMessenger, new CameraInfoHostApiImpl(instanceManager));
39-
GeneratedCameraXLibrary.JavaObjectHostApi.setup(
40-
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
4140
GeneratedCameraXLibrary.CameraSelectorHostApi.setup(
4241
binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager));
42+
GeneratedCameraXLibrary.JavaObjectHostApi.setup(
43+
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
4344
processCameraProviderHostApi =
4445
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
4546
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
@@ -49,10 +50,6 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
4950
@Override
5051
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
5152
pluginBinding = flutterPluginBinding;
52-
(new CameraAndroidCameraxPlugin())
53-
.setUp(
54-
flutterPluginBinding.getBinaryMessenger(),
55-
flutterPluginBinding.getApplicationContext());
5653
}
5754

5855
@Override
@@ -66,7 +63,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
6663

6764
@Override
6865
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
69-
updateContext(activityPluginBinding.getActivity());
66+
setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext());
67+
updateContext(pluginBinding.getApplicationContext());
68+
processCameraProviderHostApi.setLifecycleOwner(
69+
(LifecycleOwner) activityPluginBinding.getActivity());
7070
}
7171

7272
@Override
@@ -89,7 +89,7 @@ public void onDetachedFromActivity() {
8989
* Updates context that is used to fetch the corresponding instance of a {@code
9090
* ProcessCameraProvider}.
9191
*/
92-
private void updateContext(Context context) {
92+
public void updateContext(Context context) {
9393
if (processCameraProviderHostApi != null) {
9494
processCameraProviderHostApi.setContext(context);
9595
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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.core.Camera;
8+
import io.flutter.plugin.common.BinaryMessenger;
9+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraFlutterApi;
10+
11+
public class CameraFlutterApiImpl extends CameraFlutterApi {
12+
private final InstanceManager instanceManager;
13+
14+
public CameraFlutterApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
15+
super(binaryMessenger);
16+
this.instanceManager = instanceManager;
17+
}
18+
19+
void create(Camera camera, Reply<Void> reply) {
20+
create(instanceManager.addHostCreatedInstance(camera), reply);
21+
}
22+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import androidx.annotation.NonNull;
88
import androidx.camera.core.CameraInfo;
99
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoHostApi;
10+
import java.util.Objects;
1011

1112
public class CameraInfoHostApiImpl implements CameraInfoHostApi {
1213
private final InstanceManager instanceManager;
@@ -17,7 +18,8 @@ public CameraInfoHostApiImpl(InstanceManager instanceManager) {
1718

1819
@Override
1920
public Long getSensorRotationDegrees(@NonNull Long identifier) {
20-
CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(identifier);
21+
CameraInfo cameraInfo =
22+
(CameraInfo) Objects.requireNonNull(instanceManager.getInstance(identifier));
2123
return Long.valueOf(cameraInfo.getSensorRotationDegrees());
2224
}
2325
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraSelectorHostApi;
1313
import java.util.ArrayList;
1414
import java.util.List;
15+
import java.util.Objects;
1516

1617
public class CameraSelectorHostApiImpl implements CameraSelectorHostApi {
1718
private final BinaryMessenger binaryMessenger;
@@ -41,13 +42,15 @@ public void create(@NonNull Long identifier, Long lensFacing) {
4142

4243
@Override
4344
public List<Long> filter(@NonNull Long identifier, @NonNull List<Long> cameraInfoIds) {
44-
CameraSelector cameraSelector = (CameraSelector) instanceManager.getInstance(identifier);
45+
CameraSelector cameraSelector =
46+
(CameraSelector) Objects.requireNonNull(instanceManager.getInstance(identifier));
4547
List<CameraInfo> cameraInfosForFilter = new ArrayList<CameraInfo>();
4648

4749
for (Number cameraInfoAsNumber : cameraInfoIds) {
4850
Long cameraInfoId = cameraInfoAsNumber.longValue();
4951

50-
CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(cameraInfoId);
52+
CameraInfo cameraInfo =
53+
(CameraInfo) Objects.requireNonNull(instanceManager.getInstance(cameraInfoId));
5154
cameraInfosForFilter.add(cameraInfo);
5255
}
5356

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

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,16 @@ public interface ProcessCameraProviderHostApi {
332332
@NonNull
333333
List<Long> getAvailableCameraInfos(@NonNull Long identifier);
334334

335+
@NonNull
336+
Long bindToLifecycle(
337+
@NonNull Long identifier,
338+
@NonNull Long cameraSelectorIdentifier,
339+
@NonNull List<Long> useCaseIds);
340+
341+
void unbind(@NonNull Long identifier, @NonNull List<Long> useCaseIds);
342+
343+
void unbindAll(@NonNull Long identifier);
344+
335345
/** The codec used by ProcessCameraProviderHostApi. */
336346
static MessageCodec<Object> getCodec() {
337347
return ProcessCameraProviderHostApiCodec.INSTANCE;
@@ -405,6 +415,107 @@ public void error(Throwable error) {
405415
channel.setMessageHandler(null);
406416
}
407417
}
418+
{
419+
BasicMessageChannel<Object> channel =
420+
new BasicMessageChannel<>(
421+
binaryMessenger,
422+
"dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle",
423+
getCodec());
424+
if (api != null) {
425+
channel.setMessageHandler(
426+
(message, reply) -> {
427+
Map<String, Object> wrapped = new HashMap<>();
428+
try {
429+
ArrayList<Object> args = (ArrayList<Object>) message;
430+
Number identifierArg = (Number) args.get(0);
431+
if (identifierArg == null) {
432+
throw new NullPointerException("identifierArg unexpectedly null.");
433+
}
434+
Number cameraSelectorIdentifierArg = (Number) args.get(1);
435+
if (cameraSelectorIdentifierArg == null) {
436+
throw new NullPointerException(
437+
"cameraSelectorIdentifierArg unexpectedly null.");
438+
}
439+
List<Long> useCaseIdsArg = (List<Long>) args.get(2);
440+
if (useCaseIdsArg == null) {
441+
throw new NullPointerException("useCaseIdsArg unexpectedly null.");
442+
}
443+
Long output =
444+
api.bindToLifecycle(
445+
(identifierArg == null) ? null : identifierArg.longValue(),
446+
(cameraSelectorIdentifierArg == null)
447+
? null
448+
: cameraSelectorIdentifierArg.longValue(),
449+
useCaseIdsArg);
450+
wrapped.put("result", output);
451+
} catch (Error | RuntimeException exception) {
452+
wrapped.put("error", wrapError(exception));
453+
}
454+
reply.reply(wrapped);
455+
});
456+
} else {
457+
channel.setMessageHandler(null);
458+
}
459+
}
460+
{
461+
BasicMessageChannel<Object> channel =
462+
new BasicMessageChannel<>(
463+
binaryMessenger,
464+
"dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind",
465+
getCodec());
466+
if (api != null) {
467+
channel.setMessageHandler(
468+
(message, reply) -> {
469+
Map<String, Object> wrapped = new HashMap<>();
470+
try {
471+
ArrayList<Object> args = (ArrayList<Object>) message;
472+
Number identifierArg = (Number) args.get(0);
473+
if (identifierArg == null) {
474+
throw new NullPointerException("identifierArg unexpectedly null.");
475+
}
476+
List<Long> useCaseIdsArg = (List<Long>) args.get(1);
477+
if (useCaseIdsArg == null) {
478+
throw new NullPointerException("useCaseIdsArg unexpectedly null.");
479+
}
480+
api.unbind(
481+
(identifierArg == null) ? null : identifierArg.longValue(), useCaseIdsArg);
482+
wrapped.put("result", null);
483+
} catch (Error | RuntimeException exception) {
484+
wrapped.put("error", wrapError(exception));
485+
}
486+
reply.reply(wrapped);
487+
});
488+
} else {
489+
channel.setMessageHandler(null);
490+
}
491+
}
492+
{
493+
BasicMessageChannel<Object> channel =
494+
new BasicMessageChannel<>(
495+
binaryMessenger,
496+
"dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll",
497+
getCodec());
498+
if (api != null) {
499+
channel.setMessageHandler(
500+
(message, reply) -> {
501+
Map<String, Object> wrapped = new HashMap<>();
502+
try {
503+
ArrayList<Object> args = (ArrayList<Object>) message;
504+
Number identifierArg = (Number) args.get(0);
505+
if (identifierArg == null) {
506+
throw new NullPointerException("identifierArg unexpectedly null.");
507+
}
508+
api.unbindAll((identifierArg == null) ? null : identifierArg.longValue());
509+
wrapped.put("result", null);
510+
} catch (Error | RuntimeException exception) {
511+
wrapped.put("error", wrapError(exception));
512+
}
513+
reply.reply(wrapped);
514+
});
515+
} else {
516+
channel.setMessageHandler(null);
517+
}
518+
}
408519
}
409520
}
410521

@@ -445,6 +556,40 @@ public void create(@NonNull Long identifierArg, Reply<Void> callback) {
445556
}
446557
}
447558

559+
private static class CameraFlutterApiCodec extends StandardMessageCodec {
560+
public static final CameraFlutterApiCodec INSTANCE = new CameraFlutterApiCodec();
561+
562+
private CameraFlutterApiCodec() {}
563+
}
564+
565+
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
566+
public static class CameraFlutterApi {
567+
private final BinaryMessenger binaryMessenger;
568+
569+
public CameraFlutterApi(BinaryMessenger argBinaryMessenger) {
570+
this.binaryMessenger = argBinaryMessenger;
571+
}
572+
573+
public interface Reply<T> {
574+
void reply(T reply);
575+
}
576+
577+
static MessageCodec<Object> getCodec() {
578+
return CameraFlutterApiCodec.INSTANCE;
579+
}
580+
581+
public void create(@NonNull Long identifierArg, Reply<Void> callback) {
582+
BasicMessageChannel<Object> channel =
583+
new BasicMessageChannel<>(
584+
binaryMessenger, "dev.flutter.pigeon.CameraFlutterApi.create", getCodec());
585+
channel.send(
586+
new ArrayList<Object>(Arrays.asList(identifierArg)),
587+
channelReply -> {
588+
callback.reply(null);
589+
});
590+
}
591+
}
592+
448593
private static Map<String, Object> wrapError(Throwable exception) {
449594
Map<String, Object> errorMap = new HashMap<>();
450595
errorMap.put("message", exception.toString());

0 commit comments

Comments
 (0)