From e60b1b1ab86b019c075773733f7fab3017e0d731 Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Mon, 7 Oct 2019 14:37:28 -0700 Subject: [PATCH 1/4] [camera] Migrate to the new embedding Migrates the camera plugin to the new embedding and adds an example activity exercising it. DO NOT MERGE until we have an e2e testing solution and are clear to land this without having any issues with stable. However this is still ready for an initial review. Unlike some of the previous PRs on this, this is the minimal migration that would land this without adding any tech debt. From previous offline discussions we'd like to scope these migrations to the bare minimum required to keep the patches small. See #2114 for a migration combined with a refactoring. That said there are still some minor changes to prevent this from adding additional tech debt, unlike #2118: - The new code has been moved from `io` to `dev`. This is for consistency with the other migrated plugins. - `#onMethodCall` has been split up into its own `MethodCallHandlerImpl` class to try and keep `CameraPlugin` from mixing concerns. Before the migration, _all_ it was doing was responding to method calls. Now there's a significant amount of logic just based around responding to lifecycle events. It still seemed better to split that into a seperate file than to try and manage that logic all in one place. Other than the above refactorings, the original logic is untouched. --- packages/camera/android/gradle.properties | 2 + .../flutter/plugins/camera/Camera.java | 4 +- .../plugins/camera/CameraPermissions.java | 29 +++-- .../flutter/plugins/camera/CameraPlugin.java | 110 ++++++++++++++++++ .../flutter/plugins/camera/CameraUtils.java | 4 +- .../flutter/plugins/camera/DartMessenger.java | 2 +- .../camera/MethodCallHandlerImpl.java} | 107 +++++++++-------- .../plugins/camera/DartMessengerTest.java | 2 +- .../android/app/src/main/AndroidManifest.xml | 58 +++++---- .../plugins/cameraexample/MainActivity.java | 22 ++++ .../camera/example/android/gradle.properties | 1 + packages/camera/pubspec.yaml | 2 +- 12 files changed, 246 insertions(+), 97 deletions(-) rename packages/camera/android/src/main/java/{io => dev}/flutter/plugins/camera/Camera.java (99%) rename packages/camera/android/src/main/java/{io => dev}/flutter/plugins/camera/CameraPermissions.java (85%) create mode 100644 packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java rename packages/camera/android/src/main/java/{io => dev}/flutter/plugins/camera/CameraUtils.java (98%) rename packages/camera/android/src/main/java/{io => dev}/flutter/plugins/camera/DartMessenger.java (97%) rename packages/camera/android/src/main/java/{io/flutter/plugins/camera/CameraPlugin.java => dev/flutter/plugins/camera/MethodCallHandlerImpl.java} (68%) rename packages/camera/android/src/test/java/{io => dev}/flutter/plugins/camera/DartMessengerTest.java (98%) create mode 100644 packages/camera/example/android/app/src/main/java/dev/flutter/plugins/cameraexample/MainActivity.java diff --git a/packages/camera/android/gradle.properties b/packages/camera/android/gradle.properties index 8bd86f680510..94adc3a3f97a 100644 --- a/packages/camera/android/gradle.properties +++ b/packages/camera/android/gradle.properties @@ -1 +1,3 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java similarity index 99% rename from packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java rename to packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java index 754a157a8b71..8e7066f347df 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java @@ -1,7 +1,7 @@ -package io.flutter.plugins.camera; +package dev.flutter.plugins.camera; import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN; -import static io.flutter.plugins.camera.CameraUtils.computeBestPreviewSize; +import static dev.flutter.plugins.camera.CameraUtils.computeBestPreviewSize; import android.annotation.SuppressLint; import android.app.Activity; diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java similarity index 85% rename from packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java rename to packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java index e45fb1e5a594..4a4d06d86c16 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java @@ -1,4 +1,4 @@ -package io.flutter.plugins.camera; +package dev.flutter.plugins.camera; import android.Manifest; import android.Manifest.permission; @@ -7,20 +7,30 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener; + +final class CameraPermissions { + interface PermissionsRegistry { + void addListener(RequestPermissionsResultListener handler); + } + + interface ResultCallback { + void onResult(String errorCode, String errorDescription); + } -public class CameraPermissions { private static final int CAMERA_REQUEST_ID = 9796; private boolean ongoing = false; - public void requestPermissions( - Registrar registrar, boolean enableAudio, ResultCallback callback) { + void requestPermissions( + Activity activity, + PermissionsRegistry permissionsRegistry, + boolean enableAudio, + ResultCallback callback) { if (ongoing) { callback.onResult("cameraPermission", "Camera permission request ongoing"); } - Activity activity = registrar.activity(); if (!hasCameraPermission(activity) || (enableAudio && !hasAudioPermission(activity))) { - registrar.addRequestPermissionsResultListener( + permissionsRegistry.addListener( new CameraRequestPermissionsListener( (String errorCode, String errorDescription) -> { ongoing = false; @@ -51,6 +61,7 @@ private boolean hasAudioPermission(Activity activity) { private static class CameraRequestPermissionsListener implements PluginRegistry.RequestPermissionsResultListener { + final ResultCallback callback; private CameraRequestPermissionsListener(ResultCallback callback) { @@ -73,8 +84,4 @@ public boolean onRequestPermissionsResult(int id, String[] permissions, int[] gr return false; } } - - interface ResultCallback { - void onResult(String errorCode, String errorDescription); - } } diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java new file mode 100644 index 000000000000..9afeae3bd939 --- /dev/null +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java @@ -0,0 +1,110 @@ +// Copyright 2019 The Chromium 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 dev.flutter.plugins.camera; + +import android.app.Activity; +import android.os.Build; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import dev.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.view.TextureRegistry; + +/** + * Plugin implementation that uses the {@code io.flutter.embedding} package. + * + *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + */ +public final class CameraPlugin implements FlutterPlugin, ActivityAware { + + private static final String TAG = "CameraPlugin"; + private @Nullable FlutterPluginBinding flutterPluginBinding; + private @Nullable MethodCallHandlerImpl methodCallHandler; + + /** + * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. + * + *

See {@code dev.flutter.plugins.camera.MainActivity} for an example. + */ + public CameraPlugin() {} + + /** + * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common} + * package. + * + *

Calling this automatically initializes the plugin. However plugins initialized this way + * won't react to changes in activity or context, unlike {@link CameraPlugin}. + */ + public static void registerWith(Registrar registrar) { + if (registrar.activity() == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // When a background flutter view tries to register the plugin, the registrar has no activity. + // We stop the registration process as this plugin is foreground only. Also, if the sdk is + // less than 21 (min sdk for Camera2) we don't register the plugin. + return; + } + + CameraPlugin plugin = new CameraPlugin(); + plugin.startListening( + registrar.activity(), + registrar.messenger(), + registrar::addRequestPermissionsResultListener, + registrar.view()); + } + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + this.flutterPluginBinding = binding; + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + this.flutterPluginBinding = null; + } + + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + startListening( + binding.getActivity(), + flutterPluginBinding.getFlutterEngine().getDartExecutor(), + binding::addRequestPermissionsResultListener, + flutterPluginBinding.getFlutterEngine().getRenderer()); + } + + @Override + public void onDetachedFromActivity() { + if (methodCallHandler == null) { + Log.wtf(TAG, "Detached before initialzed."); + return; + } + + methodCallHandler.stopListening(); + methodCallHandler = null; + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + onAttachedToActivity(binding); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity(); + } + + void startListening( + Activity activity, + BinaryMessenger messenger, + PermissionsRegistry permissionsRegistry, + TextureRegistry textureRegistry) { + methodCallHandler = + new MethodCallHandlerImpl( + activity, messenger, new CameraPermissions(), permissionsRegistry, textureRegistry); + } +} diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java similarity index 98% rename from packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java rename to packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java index a7bb3b7d4914..2095b18441a2 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java @@ -1,4 +1,4 @@ -package io.flutter.plugins.camera; +package dev.flutter.plugins.camera; import android.app.Activity; import android.content.Context; @@ -10,7 +10,7 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.media.CamcorderProfile; import android.util.Size; -import io.flutter.plugins.camera.Camera.ResolutionPreset; +import dev.flutter.plugins.camera.Camera.ResolutionPreset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java similarity index 97% rename from packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java rename to packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java index fe385bef7818..f100714a9273 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java @@ -1,4 +1,4 @@ -package io.flutter.plugins.camera; +package dev.flutter.plugins.camera; import android.text.TextUtils; import androidx.annotation.Nullable; diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java similarity index 68% rename from packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java rename to packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java index b504f039e326..3b4671f58ff5 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -1,67 +1,42 @@ -// Copyright 2019 The Chromium 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.camera; +package dev.flutter.plugins.camera; +import android.app.Activity; import android.hardware.camera2.CameraAccessException; -import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import dev.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry.Registrar; -import io.flutter.view.FlutterView; import io.flutter.view.TextureRegistry; -public class CameraPlugin implements MethodCallHandler { - - private final CameraPermissions cameraPermissions = new CameraPermissions(); - private final FlutterView view; - private final Registrar registrar; +final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { + private final Activity activity; + private final BinaryMessenger messenger; + private final CameraPermissions cameraPermissions; + private final PermissionsRegistry permissionsRegistry; + private final TextureRegistry textureRegistry; + private final MethodChannel methodChannel; private final EventChannel imageStreamChannel; - private Camera camera; - - private CameraPlugin(Registrar registrar) { - this.registrar = registrar; - this.view = registrar.view(); - this.imageStreamChannel = - new EventChannel(registrar.messenger(), "plugins.flutter.io/camera/imageStream"); - } - - public static void registerWith(Registrar registrar) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // When a background flutter view tries to register the plugin, the registrar has no activity. - // We stop the registration process as this plugin is foreground only. Also, if the sdk is - // less than 21 (min sdk for Camera2) we don't register the plugin. - return; - } - - final MethodChannel channel = - new MethodChannel(registrar.messenger(), "plugins.flutter.io/camera"); + private @Nullable Camera camera; - channel.setMethodCallHandler(new CameraPlugin(registrar)); - } - - private void instantiateCamera(MethodCall call, Result result) throws CameraAccessException { - String cameraName = call.argument("cameraName"); - String resolutionPreset = call.argument("resolutionPreset"); - boolean enableAudio = call.argument("enableAudio"); - TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = view.createSurfaceTexture(); - DartMessenger dartMessenger = - new DartMessenger(registrar.messenger(), flutterSurfaceTexture.id()); - camera = - new Camera( - registrar.activity(), - flutterSurfaceTexture, - dartMessenger, - cameraName, - resolutionPreset, - enableAudio); + MethodCallHandlerImpl( + Activity activity, + BinaryMessenger messenger, + CameraPermissions cameraPermissions, + PermissionsRegistry permissionsAdder, + TextureRegistry textureRegistry) { + this.activity = activity; + this.messenger = messenger; + this.cameraPermissions = cameraPermissions; + this.permissionsRegistry = permissionsAdder; + this.textureRegistry = textureRegistry; - camera.open(result); + methodChannel = new MethodChannel(messenger, "plugins.flutter.io/camera"); + imageStreamChannel = new EventChannel(messenger, "plugins.flutter.io/camera/imageStream"); + methodChannel.setMethodCallHandler(this); } @Override @@ -69,7 +44,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) switch (call.method) { case "availableCameras": try { - result.success(CameraUtils.getAvailableCameras(registrar.activity())); + result.success(CameraUtils.getAvailableCameras(activity)); } catch (Exception e) { handleException(e, result); } @@ -80,7 +55,8 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) camera.close(); } cameraPermissions.requestPermissions( - registrar, + activity, + permissionsRegistry, call.argument("enableAudio"), (String errCode, String errDesc) -> { if (errCode == null) { @@ -161,6 +137,29 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) } } + void stopListening() { + methodChannel.setMethodCallHandler(null); + } + + private void instantiateCamera(MethodCall call, Result result) throws CameraAccessException { + String cameraName = call.argument("cameraName"); + String resolutionPreset = call.argument("resolutionPreset"); + boolean enableAudio = call.argument("enableAudio"); + TextureRegistry.SurfaceTextureEntry flutterSurfaceTexture = + textureRegistry.createSurfaceTexture(); + DartMessenger dartMessenger = new DartMessenger(messenger, flutterSurfaceTexture.id()); + camera = + new Camera( + activity, + flutterSurfaceTexture, + dartMessenger, + cameraName, + resolutionPreset, + enableAudio); + + camera.open(result); + } + // We move catching CameraAccessException out of onMethodCall because it causes a crash // on plugin registration for sdks incompatible with Camera2 (< 21). We want this plugin to // to be able to compile with <21 sdks for apps that want the camera and support earlier version. diff --git a/packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java b/packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java similarity index 98% rename from packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java rename to packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java index db89eb279f41..31fcd67b470e 100644 --- a/packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java +++ b/packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java @@ -1,4 +1,4 @@ -package io.flutter.plugins.camera; +package dev.flutter.plugins.camera; import static junit.framework.TestCase.assertNull; import static org.junit.Assert.assertEquals; diff --git a/packages/camera/example/android/app/src/main/AndroidManifest.xml b/packages/camera/example/android/app/src/main/AndroidManifest.xml index 15f6087e4ebe..38c92e88e76d 100644 --- a/packages/camera/example/android/app/src/main/AndroidManifest.xml +++ b/packages/camera/example/android/app/src/main/AndroidManifest.xml @@ -1,30 +1,38 @@ + package="io.flutter.plugins.cameraexample"> - + + + + + + + + + + - + - - - - - - - - - + diff --git a/packages/camera/example/android/app/src/main/java/dev/flutter/plugins/cameraexample/MainActivity.java b/packages/camera/example/android/app/src/main/java/dev/flutter/plugins/cameraexample/MainActivity.java new file mode 100644 index 000000000000..121185f485e2 --- /dev/null +++ b/packages/camera/example/android/app/src/main/java/dev/flutter/plugins/cameraexample/MainActivity.java @@ -0,0 +1,22 @@ +package dev.flutter.plugins.cameraexample; + +import androidx.annotation.NonNull; +import dev.flutter.plugins.camera.CameraPlugin; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry; +import io.flutter.plugins.pathprovider.PathProviderPlugin; +import io.flutter.plugins.videoplayer.VideoPlayerPlugin; + +public class MainActivity extends FlutterActivity { + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + flutterEngine.getPlugins().add(new CameraPlugin()); + + ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine); + PathProviderPlugin.registerWith( + shimPluginRegistry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin")); + VideoPlayerPlugin.registerWith( + shimPluginRegistry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin")); + } +} diff --git a/packages/camera/example/android/gradle.properties b/packages/camera/example/android/gradle.properties index 8bd86f680510..7be3d8b46841 100644 --- a/packages/camera/example/android/gradle.properties +++ b/packages/camera/example/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index 0906f175fb8d..6a0b7c072620 100644 --- a/packages/camera/pubspec.yaml +++ b/packages/camera/pubspec.yaml @@ -27,7 +27,7 @@ dev_dependencies: flutter: plugin: - androidPackage: io.flutter.plugins.camera + androidPackage: dev.flutter.plugins.camera pluginClass: CameraPlugin environment: From 479e3590bbf71854421da663602aa4c67efd35bf Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Tue, 8 Oct 2019 13:04:02 -0700 Subject: [PATCH 2/4] Naming changes from offline discussion. --- .../flutter/plugins/camera/Camera.java | 4 ++-- .../plugins/camera/CameraPermissions.java | 2 +- .../flutter/plugins/camera/CameraPlugin.java | 6 ++--- .../flutter/plugins/camera/CameraUtils.java | 4 ++-- .../flutter/plugins/camera/DartMessenger.java | 2 +- .../plugins/camera/MethodCallHandlerImpl.java | 4 ++-- .../plugins/camera/DartMessengerTest.java | 2 +- .../android/app/src/main/AndroidManifest.xml | 15 +++++++------ .../plugins/cameraexample/MainActivity.java | 22 ------------------- .../cameraexample/EmbeddingV1Activity.java | 13 +++++++++++ .../plugins/cameraexample/MainActivity.java | 21 +++++++++++++----- packages/camera/pubspec.yaml | 2 +- 12 files changed, 49 insertions(+), 48 deletions(-) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/Camera.java (99%) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/CameraPermissions.java (98%) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/CameraPlugin.java (95%) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/CameraUtils.java (98%) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/DartMessenger.java (97%) rename packages/camera/android/src/main/java/{dev => io}/flutter/plugins/camera/MethodCallHandlerImpl.java (98%) rename packages/camera/android/src/test/java/{dev => io}/flutter/plugins/camera/DartMessengerTest.java (98%) delete mode 100644 packages/camera/example/android/app/src/main/java/dev/flutter/plugins/cameraexample/MainActivity.java create mode 100644 packages/camera/example/android/app/src/main/java/io/flutter/plugins/cameraexample/EmbeddingV1Activity.java diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java similarity index 99% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index 8e7066f347df..754a157a8b71 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/Camera.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -1,7 +1,7 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN; -import static dev.flutter.plugins.camera.CameraUtils.computeBestPreviewSize; +import static io.flutter.plugins.camera.CameraUtils.computeBestPreviewSize; import android.annotation.SuppressLint; import android.app.Activity; diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java similarity index 98% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java index 4a4d06d86c16..3c86ce0d9816 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPermissions.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java @@ -1,4 +1,4 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import android.Manifest; import android.Manifest.permission; diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java similarity index 95% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index 9afeae3bd939..fb709e7e6054 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import android.app.Activity; import android.os.Build; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import dev.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.view.TextureRegistry; /** @@ -31,7 +31,7 @@ public final class CameraPlugin implements FlutterPlugin, ActivityAware { /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. * - *

See {@code dev.flutter.plugins.camera.MainActivity} for an example. + *

See {@code io.flutter.plugins.camera.MainActivity} for an example. */ public CameraPlugin() {} diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java similarity index 98% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java index 2095b18441a2..a7bb3b7d4914 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/CameraUtils.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java @@ -1,4 +1,4 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import android.app.Activity; import android.content.Context; @@ -10,7 +10,7 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.media.CamcorderProfile; import android.util.Size; -import dev.flutter.plugins.camera.Camera.ResolutionPreset; +import io.flutter.plugins.camera.Camera.ResolutionPreset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java similarity index 97% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java index f100714a9273..fe385bef7818 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/DartMessenger.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java @@ -1,4 +1,4 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import android.text.TextUtils; import androidx.annotation.Nullable; diff --git a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java similarity index 98% rename from packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java rename to packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 3b4671f58ff5..cb58d19a9a02 100644 --- a/packages/camera/android/src/main/java/dev/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -1,15 +1,15 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import android.app.Activity; import android.hardware.camera2.CameraAccessException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import dev.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry; import io.flutter.view.TextureRegistry; final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { diff --git a/packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java b/packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java similarity index 98% rename from packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java rename to packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java index 31fcd67b470e..db89eb279f41 100644 --- a/packages/camera/android/src/test/java/dev/flutter/plugins/camera/DartMessengerTest.java +++ b/packages/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java @@ -1,4 +1,4 @@ -package dev.flutter.plugins.camera; +package io.flutter.plugins.camera; import static junit.framework.TestCase.assertNull; import static org.junit.Assert.assertEquals; diff --git a/packages/camera/example/android/app/src/main/AndroidManifest.xml b/packages/camera/example/android/app/src/main/AndroidManifest.xml index 38c92e88e76d..7f28f2858649 100644 --- a/packages/camera/example/android/app/src/main/AndroidManifest.xml +++ b/packages/camera/example/android/app/src/main/AndroidManifest.xml @@ -9,13 +9,9 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" android:launchMode="singleTop" - android:name=".MainActivity" + android:name=".EmbeddingV1Activity" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize"> - - - - @@ -25,9 +21,14 @@ android:exported="true" android:hardwareAccelerated="true" android:launchMode="singleTop" - android:name="dev.flutter.plugins.cameraexample.MainActivity" + android:name=".MainActivity" android:theme="@style/LaunchTheme" - android:windowSoftInputMode="adjustResize"/> + android:windowSoftInputMode="adjustResize"> + + + + + Date: Wed, 9 Oct 2019 10:11:31 -0700 Subject: [PATCH 3/4] Review feedback --- .../flutter/plugins/camera/CameraPlugin.java | 27 ++++++++++--------- .../android/app/src/main/AndroidManifest.xml | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index fb709e7e6054..9bd34e17aa02 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -6,7 +6,6 @@ import android.app.Activity; import android.os.Build; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -18,9 +17,13 @@ import io.flutter.view.TextureRegistry; /** - * Plugin implementation that uses the {@code io.flutter.embedding} package. + * Platform implementation of the camera_plugin. * *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + * See {@code io.flutter.plugins.camera.MainActivity} for an example. + * + *

Call {@link #registerWith(Registrar)} to register an implementation of this that uses the + * stable {@code io.flutter.plugin.common} package. */ public final class CameraPlugin implements FlutterPlugin, ActivityAware { @@ -43,15 +46,8 @@ public CameraPlugin() {} * won't react to changes in activity or context, unlike {@link CameraPlugin}. */ public static void registerWith(Registrar registrar) { - if (registrar.activity() == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // When a background flutter view tries to register the plugin, the registrar has no activity. - // We stop the registration process as this plugin is foreground only. Also, if the sdk is - // less than 21 (min sdk for Camera2) we don't register the plugin. - return; - } - CameraPlugin plugin = new CameraPlugin(); - plugin.startListening( + plugin.maybeStartListening( registrar.activity(), registrar.messenger(), registrar::addRequestPermissionsResultListener, @@ -70,7 +66,7 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - startListening( + maybeStartListening( binding.getActivity(), flutterPluginBinding.getFlutterEngine().getDartExecutor(), binding::addRequestPermissionsResultListener, @@ -80,7 +76,7 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { @Override public void onDetachedFromActivity() { if (methodCallHandler == null) { - Log.wtf(TAG, "Detached before initialzed."); + // Could be on too low of an SDK to have started listening originally. return; } @@ -98,11 +94,16 @@ public void onDetachedFromActivityForConfigChanges() { onDetachedFromActivity(); } - void startListening( + private void maybeStartListening( Activity activity, BinaryMessenger messenger, PermissionsRegistry permissionsRegistry, TextureRegistry textureRegistry) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // If the sdk is less than 21 (min sdk for Camera2) we don't register the plugin. + return; + } + methodCallHandler = new MethodCallHandlerImpl( activity, messenger, new CameraPermissions(), permissionsRegistry, textureRegistry); diff --git a/packages/camera/example/android/app/src/main/AndroidManifest.xml b/packages/camera/example/android/app/src/main/AndroidManifest.xml index 7f28f2858649..aad8d98bfa27 100644 --- a/packages/camera/example/android/app/src/main/AndroidManifest.xml +++ b/packages/camera/example/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" android:launchMode="singleTop" + android:exported="true" android:name=".EmbeddingV1Activity" android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize"> @@ -18,7 +19,6 @@ Date: Mon, 14 Oct 2019 17:58:12 -0700 Subject: [PATCH 4/4] Last migration fixes - Update version in pubspec and changelog. - Migrate tests to use e2e. Make them runnable (in at least one case). - Add dynamic Gradle include script. --- packages/camera/CHANGELOG.md | 5 ++ packages/camera/android/build.gradle | 25 +++++++++ .../camera/example/android/app/build.gradle | 5 +- .../EmbeddingV1ActivityTest.java | 13 +++++ .../cameraexample/MainActivityTest.java | 11 ++++ .../camera/example/android/gradle.properties | 2 + packages/camera/example/pubspec.yaml | 5 ++ .../{camera.dart => camera_e2e.dart} | 21 +++---- .../example/test_driver/camera_e2e_test.dart | 55 +++++++++++++++++++ .../example/test_driver/camera_test.dart | 7 --- packages/camera/pubspec.yaml | 4 +- 11 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/EmbeddingV1ActivityTest.java create mode 100644 packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/MainActivityTest.java rename packages/camera/example/test_driver/{camera.dart => camera_e2e.dart} (94%) create mode 100644 packages/camera/example/test_driver/camera_e2e_test.dart delete mode 100644 packages/camera/example/test_driver/camera_test.dart diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md index 2fe0e44fa6c2..14d2289d99b1 100644 --- a/packages/camera/CHANGELOG.md +++ b/packages/camera/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.5.6 + +* Add support for the v2 Android embedding. This shouldn't affect existing + functionality. + ## 0.5.5+1 * Fix event type check diff --git a/packages/camera/android/build.gradle b/packages/camera/android/build.gradle index ab2fc8fd89f0..ebd355385c1c 100644 --- a/packages/camera/android/build.gradle +++ b/packages/camera/android/build.gradle @@ -60,3 +60,28 @@ android { dependencies { testImplementation 'junit:junit:4.12' } + +// TODO(mklim): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348 +afterEvaluate { + def containsEmbeddingDependencies = false + for (def configuration : configurations.all) { + for (def dependency : configuration.dependencies) { + if (dependency.group == 'io.flutter' && + dependency.name.startsWith('flutter_embedding') && + dependency.isTransitive()) + { + containsEmbeddingDependencies = true + break + } + } + } + if (!containsEmbeddingDependencies) { + android { + dependencies { + def lifecycle_version = "2.1.0" + api "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + api "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + } + } + } +} diff --git a/packages/camera/example/android/app/build.gradle b/packages/camera/example/android/app/build.gradle index 39003759e4a3..e47b6db5e21e 100644 --- a/packages/camera/example/android/app/build.gradle +++ b/packages/camera/example/android/app/build.gradle @@ -58,6 +58,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/EmbeddingV1ActivityTest.java b/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/EmbeddingV1ActivityTest.java new file mode 100644 index 000000000000..95b5f4373b62 --- /dev/null +++ b/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/EmbeddingV1ActivityTest.java @@ -0,0 +1,13 @@ +package io.flutter.plugins.cameraexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class EmbeddingV1ActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(EmbeddingV1Activity.class); +} diff --git a/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/MainActivityTest.java b/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/MainActivityTest.java new file mode 100644 index 000000000000..5d1b95578dc0 --- /dev/null +++ b/packages/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/MainActivityTest.java @@ -0,0 +1,11 @@ +package io.flutter.plugins.cameraexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} diff --git a/packages/camera/example/android/gradle.properties b/packages/camera/example/android/gradle.properties index 7be3d8b46841..a6738207fd15 100644 --- a/packages/camera/example/android/gradle.properties +++ b/packages/camera/example/android/gradle.properties @@ -1,2 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true android.enableR8=true diff --git a/packages/camera/example/pubspec.yaml b/packages/camera/example/pubspec.yaml index 59f3821abe21..0f76a09fed3b 100644 --- a/packages/camera/example/pubspec.yaml +++ b/packages/camera/example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter video_player: ^0.10.0 + e2e: "^0.2.0" dev_dependencies: flutter_test: @@ -18,3 +19,7 @@ dev_dependencies: flutter: uses-material-design: true + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/camera/example/test_driver/camera.dart b/packages/camera/example/test_driver/camera_e2e.dart similarity index 94% rename from packages/camera/example/test_driver/camera.dart rename to packages/camera/example/test_driver/camera_e2e.dart index d68b8c5ba1fc..2e6a344c7d04 100644 --- a/packages/camera/example/test_driver/camera.dart +++ b/packages/camera/example/test_driver/camera_e2e.dart @@ -3,16 +3,16 @@ import 'dart:io'; import 'dart:ui'; import 'package:flutter/painting.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:camera/camera.dart'; import 'package:path_provider/path_provider.dart'; import 'package:video_player/video_player.dart'; +import 'package:e2e/e2e.dart'; void main() { - final Completer completer = Completer(); Directory testDir; - enableFlutterDriverExtension(handler: (_) => completer.future); + + E2EWidgetsFlutterBinding.ensureInitialized(); setUpAll(() async { final Directory extDir = await getTemporaryDirectory(); @@ -21,7 +21,6 @@ void main() { tearDownAll(() async { await testDir.delete(recursive: true); - completer.complete(null); }); final Map presetExpectedSizes = @@ -70,7 +69,8 @@ void main() { expectedSize, Size(image.height.toDouble(), image.width.toDouble())); } - test('Capture specific image resolutions', () async { + testWidgets('Capture specific image resolutions', + (WidgetTester tester) async { final List cameras = await availableCameras(); if (cameras.isEmpty) { return; @@ -90,7 +90,7 @@ void main() { await controller.dispose(); } } - }); + }, skip: !Platform.isAndroid); // This tests that the capture is no bigger than the preset, since we have // automatic code to fall back to smaller sizes when we need to. Returns @@ -121,7 +121,8 @@ void main() { expectedSize, Size(video.height, video.width)); } - test('Capture specific video resolutions', () async { + testWidgets('Capture specific video resolutions', + (WidgetTester tester) async { final List cameras = await availableCameras(); if (cameras.isEmpty) { return; @@ -142,9 +143,9 @@ void main() { await controller.dispose(); } } - }); + }, skip: !Platform.isAndroid); - test('Pause and resume video recording', () async { + testWidgets('Pause and resume video recording', (WidgetTester tester) async { final List cameras = await availableCameras(); if (cameras.isEmpty) { return; @@ -198,5 +199,5 @@ void main() { await videoController.dispose(); expect(duration, lessThan(recordingTime - timePaused)); - }); + }, skip: !Platform.isAndroid); } diff --git a/packages/camera/example/test_driver/camera_e2e_test.dart b/packages/camera/example/test_driver/camera_e2e_test.dart new file mode 100644 index 000000000000..e3e089a81fc9 --- /dev/null +++ b/packages/camera/example/test_driver/camera_e2e_test.dart @@ -0,0 +1,55 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +const String _examplePackage = 'io.flutter.plugins.cameraexample'; + +Future main() async { + if (!(Platform.isLinux || Platform.isMacOS)) { + print('This test must be run on a POSIX host. Skipping...'); + exit(0); + } + final bool adbExists = + Process.runSync('which', ['adb']).exitCode == 0; + if (!adbExists) { + print('This test needs ADB to exist on the \$PATH. Skipping...'); + exit(0); + } + print('Granting camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + print('Starting test.'); + final FlutterDriver driver = await FlutterDriver.connect(); + final String result = + await driver.requestData(null, timeout: const Duration(minutes: 1)); + driver.close(); + print('Test finished. Revoking camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + exit(result == 'pass' ? 0 : 1); +} diff --git a/packages/camera/example/test_driver/camera_test.dart b/packages/camera/example/test_driver/camera_test.dart deleted file mode 100644 index 38fe6c447e05..000000000000 --- a/packages/camera/example/test_driver/camera_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:flutter_driver/flutter_driver.dart'; - -Future main() async { - final FlutterDriver driver = await FlutterDriver.connect(); - await driver.requestData(null, timeout: const Duration(minutes: 1)); - driver.close(); -} diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index 0906f175fb8d..82facbc94e5d 100644 --- a/packages/camera/pubspec.yaml +++ b/packages/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.5.5+1 +version: 0.5.6 authors: - Flutter Team @@ -32,4 +32,4 @@ flutter: environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" - flutter: ">=1.2.0 <2.0.0" + flutter: ">=1.6.7 <2.0.0"