From 2c04e86744c00b4578639dd4b6573b94315a706c Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 3 Aug 2022 09:44:34 -0700 Subject: [PATCH 01/28] Add camerax plugin --- .ci/flutter_master.version | 2 +- .../camera/camera_android_camerax/.gitignore | 30 + .../camera/camera_android_camerax/.metadata | 30 + .../camera_android_camerax/CHANGELOG.md | 3 + .../camera/camera_android_camerax/LICENSE | 1 + .../camera/camera_android_camerax/README.md | 15 + .../analysis_options.yaml | 4 + .../camera_android_camerax/android/.gitignore | 9 + .../android/build.gradle | 35 + .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../CameraAndroidCameraxPlugin.java | 38 + .../camera_android_camerax/example/.gitignore | 44 + .../camera_android_camerax/example/README.md | 16 + .../example/analysis_options.yaml | 29 + .../example/android/.gitignore | 13 + .../example/android/app/build.gradle | 59 + .../android/app/src/debug/AndroidManifest.xml | 8 + .../android/app/src/main/AndroidManifest.xml | 34 + .../MainActivity.java | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 8 + .../example/android/build.gradle | 31 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 11 + .../example/lib/main.dart | 63 + .../example/pubspec.yaml | 84 ++ .../example/test/widget_test.dart | 27 + .../lib/camera_android_camerax.dart | 8 + ...camera_android_camerax_method_channel.dart | 17 + ...ra_android_camerax_platform_interface.dart | 29 + .../camera_android_camerax/pubspec.yaml | 70 ++ ...a_android_camerax_method_channel_test.dart | 24 + .../test/camera_android_camerax_test.dart | 29 + packages/camera/camera_windows/CHANGELOG.md | 10 + .../camera_windows/lib/camera_windows.dart | 2 +- packages/camera/camera_windows/pubspec.yaml | 2 +- .../camera/camera_windows/windows/camera.cpp | 75 +- .../camera/camera_windows/windows/camera.h | 30 +- .../windows/capture_controller.cpp | 191 +-- .../windows/capture_controller.h | 17 +- .../windows/capture_controller_listener.h | 48 +- .../camera_windows/windows/photo_handler.cpp | 14 +- .../camera_windows/windows/photo_handler.h | 8 +- .../windows/preview_handler.cpp | 22 +- .../camera_windows/windows/preview_handler.h | 12 +- .../camera_windows/windows/record_handler.cpp | 25 +- .../camera_windows/windows/record_handler.h | 14 +- .../windows/test/camera_test.cpp | 186 ++- .../windows/test/capture_controller_test.cpp | 1103 +++++++++++++++-- .../camera_windows/windows/test/mocks.h | 34 +- .../CHANGELOG.md | 4 + .../method_channel_image_picker.dart | 1 - .../image_picker_platform.dart | 1 - .../lib/src/types/types.dart | 2 + .../pubspec.yaml | 2 +- .../new_method_channel_image_picker_test.dart | 3 - packages/local_auth/local_auth/CHANGELOG.md | 4 + packages/local_auth/local_auth/README.md | 4 +- .../android/app/src/main/AndroidManifest.xml | 2 +- packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth_android/CHANGELOG.md | 4 + .../local_auth_android/android/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 2 +- .../local_auth_android/pubspec.yaml | 2 +- .../url_launcher/lib/src/link.dart | 2 +- .../webview_flutter_android/CHANGELOG.md | 5 + .../lib/src/android_webview.dart | 5 + .../lib/src/android_webview_api_impls.dart | 297 +++-- .../lib/src/instance_manager.dart | 188 ++- .../lib/webview_android.dart | 5 +- .../lib/webview_surface_android.dart | 5 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 58 +- .../test/android_webview_test.mocks.dart | 5 - .../test/instance_manager_test.dart | 140 ++- .../test/webview_android_widget_test.dart | 2 +- .../webview_android_widget_test.mocks.dart | 3 - script/tool/README.md | 2 + .../lib/src/common/package_state_utils.dart | 2 +- 89 files changed, 2806 insertions(+), 592 deletions(-) create mode 100644 packages/camera/camera_android_camerax/.gitignore create mode 100644 packages/camera/camera_android_camerax/.metadata create mode 100644 packages/camera/camera_android_camerax/CHANGELOG.md create mode 100644 packages/camera/camera_android_camerax/LICENSE create mode 100644 packages/camera/camera_android_camerax/README.md create mode 100644 packages/camera/camera_android_camerax/analysis_options.yaml create mode 100644 packages/camera/camera_android_camerax/android/.gitignore create mode 100644 packages/camera/camera_android_camerax/android/build.gradle create mode 100644 packages/camera/camera_android_camerax/android/settings.gradle create mode 100644 packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java create mode 100644 packages/camera/camera_android_camerax/example/.gitignore create mode 100644 packages/camera/camera_android_camerax/example/README.md create mode 100644 packages/camera/camera_android_camerax/example/analysis_options.yaml create mode 100644 packages/camera/camera_android_camerax/example/android/.gitignore create mode 100644 packages/camera/camera_android_camerax/example/android/app/build.gradle create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/build.gradle create mode 100644 packages/camera/camera_android_camerax/example/android/gradle.properties create mode 100644 packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android_camerax/example/android/settings.gradle create mode 100644 packages/camera/camera_android_camerax/example/lib/main.dart create mode 100644 packages/camera/camera_android_camerax/example/pubspec.yaml create mode 100644 packages/camera/camera_android_camerax/example/test/widget_test.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart create mode 100644 packages/camera/camera_android_camerax/pubspec.yaml create mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 38ac12d087d3..e677ec80ecb8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1aa44652e4c00e62bdd57d7de49495feee6f1f1a +183d3e410102f4347eedce60950379d86991323a diff --git a/packages/camera/camera_android_camerax/.gitignore b/packages/camera/camera_android_camerax/.gitignore new file mode 100644 index 000000000000..96486fd93024 --- /dev/null +++ b/packages/camera/camera_android_camerax/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/packages/camera/camera_android_camerax/.metadata b/packages/camera/camera_android_camerax/.metadata new file mode 100644 index 000000000000..1667b9356657 --- /dev/null +++ b/packages/camera/camera_android_camerax/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + channel: spellcheck_1_1 + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + base_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + - platform: android + create_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + base_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md new file mode 100644 index 000000000000..41cc7d8192ec --- /dev/null +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/camera/camera_android_camerax/LICENSE b/packages/camera/camera_android_camerax/LICENSE new file mode 100644 index 000000000000..ba75c69f7f21 --- /dev/null +++ b/packages/camera/camera_android_camerax/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md new file mode 100644 index 000000000000..678d0abff73f --- /dev/null +++ b/packages/camera/camera_android_camerax/README.md @@ -0,0 +1,15 @@ +# camera_android_camerax + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/packages/camera/camera_android_camerax/analysis_options.yaml b/packages/camera/camera_android_camerax/analysis_options.yaml new file mode 100644 index 000000000000..a5744c1cfbe7 --- /dev/null +++ b/packages/camera/camera_android_camerax/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/camera/camera_android_camerax/android/.gitignore b/packages/camera/camera_android_camerax/android/.gitignore new file mode 100644 index 000000000000..161bdcdaf88c --- /dev/null +++ b/packages/camera/camera_android_camerax/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle new file mode 100644 index 000000000000..cf48a4bfe804 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -0,0 +1,35 @@ +group 'io.flutter.plugins.camera_android_camerax' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 16 + } +} diff --git a/packages/camera/camera_android_camerax/android/settings.gradle b/packages/camera/camera_android_camerax/android/settings.gradle new file mode 100644 index 000000000000..613f994165a0 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'camera_android_camerax' diff --git a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..531d101a2619 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java new file mode 100644 index 000000000000..4663357a5865 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java @@ -0,0 +1,38 @@ +package io.flutter.plugins.camera_android_camerax; + +import androidx.annotation.NonNull; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +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; + +/** CameraAndroidCameraxPlugin */ +public class CameraAndroidCameraxPlugin implements FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if (call.method.equals("getPlatformVersion")) { + result.success("Android " + android.os.Build.VERSION.RELEASE); + } else { + result.notImplemented(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } +} diff --git a/packages/camera/camera_android_camerax/example/.gitignore b/packages/camera/camera_android_camerax/example/.gitignore new file mode 100644 index 000000000000..24476c5d1eb5 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/camera/camera_android_camerax/example/README.md b/packages/camera/camera_android_camerax/example/README.md new file mode 100644 index 000000000000..e3817a807e77 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/README.md @@ -0,0 +1,16 @@ +# camera_android_camerax_example + +Demonstrates how to use the camera_android_camerax plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/camera/camera_android_camerax/example/analysis_options.yaml b/packages/camera/camera_android_camerax/example/analysis_options.yaml new file mode 100644 index 000000000000..61b6c4de17c9 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/camera/camera_android_camerax/example/android/.gitignore b/packages/camera/camera_android_camerax/example/android/.gitignore new file mode 100644 index 000000000000..6f568019d3c6 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle new file mode 100644 index 000000000000..8a96215d32af --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -0,0 +1,59 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "io.flutter.plugins.camera_android_camerax_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..51f3479db75b --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..363f1d69cb5b --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java new file mode 100644 index 000000000000..1886d5c8c2b6 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java @@ -0,0 +1,6 @@ +package io.flutter.plugins.camera_android_camerax_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000000..51f3479db75b --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android_camerax/example/android/build.gradle b/packages/camera/camera_android_camerax/example/android/build.gradle new file mode 100644 index 000000000000..58a8c74b1474 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/camera/camera_android_camerax/example/android/gradle.properties b/packages/camera/camera_android_camerax/example/android/gradle.properties new file mode 100644 index 000000000000..94adc3a3f97a --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..3c472b99c6f3 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/packages/camera/camera_android_camerax/example/android/settings.gradle b/packages/camera/camera_android_camerax/example/android/settings.gradle new file mode 100644 index 000000000000..44e62bcf06ae --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart new file mode 100644 index 000000000000..70475c8831b2 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:camera_android_camerax/camera_android_camerax.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + final _cameraAndroidCameraxPlugin = CameraAndroidCamerax(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await _cameraAndroidCameraxPlugin.getPlatformVersion() ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml new file mode 100644 index 000000000000..19fffbeaf721 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: camera_android_camerax_example +description: Demonstrates how to use the camera_android_camerax plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.19.0-55.0.dev <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + camera_android_camerax: + # When depending on this package from a real application you should use: + # camera_android_camerax: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/camera/camera_android_camerax/example/test/widget_test.dart b/packages/camera/camera_android_camerax/example/test/widget_test.dart new file mode 100644 index 000000000000..a657ebef4722 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:camera_android_camerax_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart new file mode 100644 index 000000000000..b4a1b762cd7f --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -0,0 +1,8 @@ + +import 'camera_android_camerax_platform_interface.dart'; + +class CameraAndroidCamerax { + Future getPlatformVersion() { + return CameraAndroidCameraxPlatform.instance.getPlatformVersion(); + } +} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart new file mode 100644 index 000000000000..d5a2dae1b92a --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'camera_android_camerax_platform_interface.dart'; + +/// An implementation of [CameraAndroidCameraxPlatform] that uses method channels. +class MethodChannelCameraAndroidCamerax extends CameraAndroidCameraxPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('camera_android_camerax'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart new file mode 100644 index 000000000000..09980acfbf94 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'camera_android_camerax_method_channel.dart'; + +abstract class CameraAndroidCameraxPlatform extends PlatformInterface { + /// Constructs a CameraAndroidCameraxPlatform. + CameraAndroidCameraxPlatform() : super(token: _token); + + static final Object _token = Object(); + + static CameraAndroidCameraxPlatform _instance = MethodChannelCameraAndroidCamerax(); + + /// The default instance of [CameraAndroidCameraxPlatform] to use. + /// + /// Defaults to [MethodChannelCameraAndroidCamerax]. + static CameraAndroidCameraxPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CameraAndroidCameraxPlatform] when + /// they register themselves. + static set instance(CameraAndroidCameraxPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml new file mode 100644 index 000000000000..4a8353fbf2dc --- /dev/null +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -0,0 +1,70 @@ +name: camera_android_camerax +description: A new Flutter plugin project. +version: 0.0.1 +homepage: + +environment: + sdk: '>=2.19.0-55.0.dev <3.0.0' + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: io.flutter.plugins.camera_android_camerax + pluginClass: CameraAndroidCameraxPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart new file mode 100644 index 000000000000..8a0176129611 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; + +void main() { + MethodChannelCameraAndroidCamerax platform = MethodChannelCameraAndroidCamerax(); + const MethodChannel channel = MethodChannel('camera_android_camerax'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart new file mode 100644 index 000000000000..0c215729942e --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:camera_android_camerax/camera_android_camerax.dart'; +import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; +import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockCameraAndroidCameraxPlatform + with MockPlatformInterfaceMixin + implements CameraAndroidCameraxPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final CameraAndroidCameraxPlatform initialPlatform = CameraAndroidCameraxPlatform.instance; + + test('$MethodChannelCameraAndroidCamerax is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + CameraAndroidCamerax cameraAndroidCameraxPlugin = CameraAndroidCamerax(); + MockCameraAndroidCameraxPlatform fakePlatform = MockCameraAndroidCameraxPlatform(); + CameraAndroidCameraxPlatform.instance = fakePlatform; + + expect(await cameraAndroidCameraxPlugin.getPlatformVersion(), '42'); + }); +} diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index a1e2a0733e1a..ef0fad497f45 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.2.0 + +**BREAKING CHANGES**: + * `CameraException.code` now has value `"CameraAccessDenied"` if camera access permission was denied. + * `CameraException.code` now has value `"camera_error"` if error occurs during capture. + +## 0.1.0+5 + +* Fixes bugs in in error handling. + ## 0.1.0+4 * Allows retrying camera initialization after error. diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index d998863d43a7..14134479994b 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -22,7 +22,7 @@ class CameraWindows extends CameraPlatform { final MethodChannel pluginChannel = const MethodChannel('plugins.flutter.io/camera_windows'); - /// Camera specific method channels to allow comminicating with specific cameras. + /// Camera specific method channels to allow communicating with specific cameras. final Map _cameraChannels = {}; /// The controller that broadcasts events coming from handleCameraMethodCall diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index e9fe0212ad80..9cf1793ad1a8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+4 +version: 0.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp index 617f98f812ab..6a0944747908 100644 --- a/packages/camera/camera_windows/windows/camera.cpp +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -16,6 +16,25 @@ constexpr char kVideoRecordedEvent[] = "video_recorded"; constexpr char kCameraClosingEvent[] = "camera_closing"; constexpr char kErrorEvent[] = "error"; +// Camera error codes +constexpr char kCameraAccessDenied[] = "CameraAccessDenied"; +constexpr char kCameraError[] = "camera_error"; +constexpr char kPluginDisposed[] = "plugin_disposed"; + +std::string GetErrorCode(CameraResult result) { + assert(result != CameraResult::kSuccess); + + switch (result) { + case CameraResult::kAccessDenied: + return kCameraAccessDenied; + + case CameraResult::kSuccess: + case CameraResult::kError: + default: + return kCameraError; + } +} + CameraImpl::CameraImpl(const std::string& device_id) : device_id_(device_id), Camera(device_id) {} @@ -24,7 +43,7 @@ CameraImpl::~CameraImpl() { OnCameraClosing(); capture_controller_ = nullptr; - SendErrorForPendingResults("plugin_disposed", + SendErrorForPendingResults(kPluginDisposed, "Plugin disposed before request was handled"); } @@ -85,9 +104,9 @@ bool CameraImpl::HasPendingResultByType(PendingResultType type) const { } void CameraImpl::SendErrorForPendingResults(const std::string& error_code, - const std::string& descripion) { + const std::string& description) { for (const auto& pending_result : pending_results_) { - pending_result.second->Error(error_code, descripion); + pending_result.second->Error(error_code, description); } pending_results_.clear(); } @@ -121,11 +140,13 @@ void CameraImpl::OnCreateCaptureEngineSucceeded(int64_t texture_id) { } } -void CameraImpl::OnCreateCaptureEngineFailed(const std::string& error) { +void CameraImpl::OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kCreateCamera); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -141,10 +162,12 @@ void CameraImpl::OnStartPreviewSucceeded(int32_t width, int32_t height) { } }; -void CameraImpl::OnStartPreviewFailed(const std::string& error) { +void CameraImpl::OnStartPreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -156,11 +179,13 @@ void CameraImpl::OnResumePreviewSucceeded() { } } -void CameraImpl::OnResumePreviewFailed(const std::string& error) { +void CameraImpl::OnResumePreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kResumePreview); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -172,11 +197,13 @@ void CameraImpl::OnPausePreviewSucceeded() { } } -void CameraImpl::OnPausePreviewFailed(const std::string& error) { +void CameraImpl::OnPausePreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kPausePreview); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -187,10 +214,12 @@ void CameraImpl::OnStartRecordSucceeded() { } }; -void CameraImpl::OnStartRecordFailed(const std::string& error) { +void CameraImpl::OnStartRecordFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -201,10 +230,12 @@ void CameraImpl::OnStopRecordSucceeded(const std::string& file_path) { } }; -void CameraImpl::OnStopRecordFailed(const std::string& error) { +void CameraImpl::OnStopRecordFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -215,11 +246,13 @@ void CameraImpl::OnTakePictureSucceeded(const std::string& file_path) { } }; -void CameraImpl::OnTakePictureFailed(const std::string& error) { +void CameraImpl::OnTakePictureFailed(CameraResult result, + const std::string& error) { auto pending_take_picture_result = GetPendingResultByType(PendingResultType::kTakePicture); if (pending_take_picture_result) { - pending_take_picture_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_take_picture_result->Error(error_code, error); } }; @@ -238,9 +271,10 @@ void CameraImpl::OnVideoRecordSucceeded(const std::string& file_path, } } -void CameraImpl::OnVideoRecordFailed(const std::string& error){}; +void CameraImpl::OnVideoRecordFailed(CameraResult result, + const std::string& error){}; -void CameraImpl::OnCaptureError(const std::string& error) { +void CameraImpl::OnCaptureError(CameraResult result, const std::string& error) { if (messenger_ && camera_id_ >= 0) { auto channel = GetMethodChannel(); @@ -250,7 +284,8 @@ void CameraImpl::OnCaptureError(const std::string& error) { channel->InvokeMethod(kErrorEvent, std::move(message_data)); } - SendErrorForPendingResults("capture_error", error); + std::string error_code = GetErrorCode(result); + SendErrorForPendingResults(error_code, error); } void CameraImpl::OnCameraClosing() { diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h index 429f41ad6315..8508da1924d0 100644 --- a/packages/camera/camera_windows/windows/camera.h +++ b/packages/camera/camera_windows/windows/camera.h @@ -87,23 +87,31 @@ class CameraImpl : public Camera { // CaptureControllerListener void OnCreateCaptureEngineSucceeded(int64_t texture_id) override; - void OnCreateCaptureEngineFailed(const std::string& error) override; + void OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) override; void OnStartPreviewSucceeded(int32_t width, int32_t height) override; - void OnStartPreviewFailed(const std::string& error) override; + void OnStartPreviewFailed(CameraResult result, + const std::string& error) override; void OnPausePreviewSucceeded() override; - void OnPausePreviewFailed(const std::string& error) override; + void OnPausePreviewFailed(CameraResult result, + const std::string& error) override; void OnResumePreviewSucceeded() override; - void OnResumePreviewFailed(const std::string& error) override; + void OnResumePreviewFailed(CameraResult result, + const std::string& error) override; void OnStartRecordSucceeded() override; - void OnStartRecordFailed(const std::string& error) override; + void OnStartRecordFailed(CameraResult result, + const std::string& error) override; void OnStopRecordSucceeded(const std::string& file_path) override; - void OnStopRecordFailed(const std::string& error) override; + void OnStopRecordFailed(CameraResult result, + const std::string& error) override; void OnTakePictureSucceeded(const std::string& file_path) override; - void OnTakePictureFailed(const std::string& error) override; + void OnTakePictureFailed(CameraResult result, + const std::string& error) override; void OnVideoRecordSucceeded(const std::string& file_path, int64_t video_duration) override; - void OnVideoRecordFailed(const std::string& error) override; - void OnCaptureError(const std::string& error) override; + void OnVideoRecordFailed(CameraResult result, + const std::string& error) override; + void OnCaptureError(CameraResult result, const std::string& error) override; // Camera bool HasDeviceId(std::string& device_id) const override { @@ -139,9 +147,9 @@ class CameraImpl : public Camera { // error ID and description. Pending results are cleared in the process. // // error_code: A string error code describing the error. - // error_message: A user-readable error message (optional). + // description: A user-readable error message (optional). void SendErrorForPendingResults(const std::string& error_code, - const std::string& descripion); + const std::string& description); // Called when camera is disposed. // Sends camera closing message to the cameras method channel. diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp index 6c89060877ef..384c86ac109b 100644 --- a/packages/camera/camera_windows/windows/capture_controller.cpp +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -22,6 +22,15 @@ namespace camera_windows { using Microsoft::WRL::ComPtr; +CameraResult GetCameraResult(HRESULT hr) { + if (SUCCEEDED(hr)) { + return CameraResult::kSuccess; + } + + return hr == E_ACCESSDENIED ? CameraResult::kAccessDenied + : CameraResult::kError; +} + CaptureControllerImpl::CaptureControllerImpl( CaptureControllerListener* listener) : capture_controller_listener_(listener), CaptureController(){}; @@ -237,6 +246,8 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { return hr; } + // Check MF_CAPTURE_ENGINE_INITIALIZED event handling + // for response process. hr = capture_engine_->Initialize(capture_engine_callback_handler_.Get(), attributes.Get(), audio_source_.Get(), video_source_.Get()); @@ -244,7 +255,7 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { } void CaptureControllerImpl::ResetCaptureController() { - if (record_handler_) { + if (record_handler_ && record_handler_->CanStop()) { if (record_handler_->IsContinuousRecording()) { StopRecord(); } else if (record_handler_->IsTimedRecording()) { @@ -295,11 +306,11 @@ bool CaptureControllerImpl::InitCaptureDevice( if (IsInitialized()) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Capture device already initialized"); + CameraResult::kError, "Capture device already initialized"); return false; } else if (capture_engine_state_ == CaptureEngineState::kInitializing) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Capture device already initializing"); + CameraResult::kError, "Capture device already initializing"); return false; } @@ -315,7 +326,7 @@ bool CaptureControllerImpl::InitCaptureDevice( if (FAILED(hr)) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create camera"); + GetCameraResult(hr), "Failed to create camera"); ResetCaptureController(); return false; } @@ -326,7 +337,7 @@ bool CaptureControllerImpl::InitCaptureDevice( HRESULT hr = CreateCaptureEngine(); if (FAILED(hr)) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create camera"); + GetCameraResult(hr), "Failed to create camera"); ResetCaptureController(); return false; } @@ -339,29 +350,34 @@ void CaptureControllerImpl::TakePicture(const std::string& file_path) { assert(capture_engine_); if (!IsInitialized()) { - return OnPicture(false, "Not initialized"); + return OnPicture(CameraResult::kError, "Not initialized"); } + HRESULT hr = S_OK; + if (!base_capture_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnPicture(false, "Failed to initialize photo capture"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnPicture(GetCameraResult(hr), + "Failed to initialize photo capture"); } } if (!photo_handler_) { photo_handler_ = std::make_unique(); } else if (photo_handler_->IsTakingPhoto()) { - return OnPicture(false, "Photo already requested"); + return OnPicture(CameraResult::kError, "Photo already requested"); } // Check MF_CAPTURE_ENGINE_PHOTO_TAKEN event handling // for response process. - if (!photo_handler_->TakePhoto(file_path, capture_engine_.Get(), - base_capture_media_type_.Get())) { + hr = photo_handler_->TakePhoto(file_path, capture_engine_.Get(), + base_capture_media_type_.Get()); + if (FAILED(hr)) { // Destroy photo handler on error cases to make sure state is resetted. photo_handler_ = nullptr; - return OnPicture(false, "Failed to take photo"); + return OnPicture(GetCameraResult(hr), "Failed to take photo"); } } @@ -391,7 +407,7 @@ uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const { } } -// Finds best mediat type for given source stream index and max height; +// Finds best media type for given source stream index and max height; bool FindBestMediaType(DWORD source_stream_index, IMFCaptureSource* source, IMFMediaType** target_media_type, uint32_t max_height, uint32_t* target_frame_width, @@ -485,15 +501,19 @@ void CaptureControllerImpl::StartRecord(const std::string& file_path, assert(capture_engine_); if (!IsInitialized()) { - return OnRecordStarted(false, + return OnRecordStarted(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } + HRESULT hr = S_OK; + if (!base_capture_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnRecordStarted(false, "Failed to initialize video recording"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnRecordStarted(GetCameraResult(hr), + "Failed to initialize video recording"); } } @@ -501,19 +521,21 @@ void CaptureControllerImpl::StartRecord(const std::string& file_path, record_handler_ = std::make_unique(record_audio_); } else if (!record_handler_->CanStart()) { return OnRecordStarted( - false, + CameraResult::kError, "Recording cannot be started. Previous recording must be stopped " "first."); } // Check MF_CAPTURE_ENGINE_RECORD_STARTED event handling for response // process. - if (!record_handler_->StartRecord(file_path, max_video_duration_ms, + hr = record_handler_->StartRecord(file_path, max_video_duration_ms, capture_engine_.Get(), - base_capture_media_type_.Get())) { + base_capture_media_type_.Get()); + if (FAILED(hr)) { // Destroy record handler on error cases to make sure state is resetted. record_handler_ = nullptr; - return OnRecordStarted(false, "Failed to start video recording"); + return OnRecordStarted(GetCameraResult(hr), + "Failed to start video recording"); } } @@ -521,21 +543,22 @@ void CaptureControllerImpl::StopRecord() { assert(capture_controller_listener_); if (!IsInitialized()) { - return OnRecordStopped(false, + return OnRecordStopped(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } if (!record_handler_ && !record_handler_->CanStop()) { - return OnRecordStopped(false, "Recording cannot be stopped."); + return OnRecordStopped(CameraResult::kError, + "Recording cannot be stopped."); } // Check MF_CAPTURE_ENGINE_RECORD_STOPPED event handling for response // process. - if (!record_handler_->StopRecord(capture_engine_.Get())) { - // Destroy record handler on error cases to make sure state is resetted. - record_handler_ = nullptr; - return OnRecordStopped(false, "Failed to stop video recording"); + HRESULT hr = record_handler_->StopRecord(capture_engine_.Get()); + if (FAILED(hr)) { + return OnRecordStopped(GetCameraResult(hr), + "Failed to stop video recording"); } } @@ -547,11 +570,12 @@ void CaptureControllerImpl::StopTimedRecord() { return; } - if (!record_handler_->StopRecord(capture_engine_.Get())) { + HRESULT hr = record_handler_->StopRecord(capture_engine_.Get()); + if (FAILED(hr)) { // Destroy record handler on error cases to make sure state is resetted. record_handler_ = nullptr; return capture_controller_listener_->OnVideoRecordFailed( - "Failed to record video"); + GetCameraResult(hr), "Failed to record video"); } } @@ -563,37 +587,46 @@ void CaptureControllerImpl::StartPreview() { assert(texture_handler_); if (!IsInitialized() || !texture_handler_) { - return OnPreviewStarted(false, + return OnPreviewStarted(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } + HRESULT hr = S_OK; + if (!base_preview_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnPreviewStarted(false, "Failed to initialize video preview"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnPreviewStarted(GetCameraResult(hr), + "Failed to initialize video preview"); } } texture_handler_->UpdateTextureSize(preview_frame_width_, preview_frame_height_); + // TODO(loic-sharma): This does not handle duplicate calls properly. + // See: https://github.com/flutter/flutter/issues/108404 if (!preview_handler_) { preview_handler_ = std::make_unique(); } else if (preview_handler_->IsInitialized()) { - return OnPreviewStarted(true, ""); + return OnPreviewStarted(CameraResult::kSuccess, ""); } else { - return OnPreviewStarted(false, "Preview already exists"); + return OnPreviewStarted(CameraResult::kError, "Preview already exists"); } // Check MF_CAPTURE_ENGINE_PREVIEW_STARTED event handling for response // process. - if (!preview_handler_->StartPreview(capture_engine_.Get(), + hr = preview_handler_->StartPreview(capture_engine_.Get(), base_preview_media_type_.Get(), - capture_engine_callback_handler_.Get())) { + capture_engine_callback_handler_.Get()); + + if (FAILED(hr)) { // Destroy preview handler on error cases to make sure state is resetted. preview_handler_ = nullptr; - return OnPreviewStarted(false, "Failed to start video preview"); + return OnPreviewStarted(GetCameraResult(hr), + "Failed to start video preview"); } } @@ -602,15 +635,15 @@ void CaptureControllerImpl::StartPreview() { // pausing and resuming the preview. // Check MF_CAPTURE_ENGINE_PREVIEW_STOPPED event handling for response // process. -void CaptureControllerImpl::StopPreview() { +HRESULT CaptureControllerImpl::StopPreview() { assert(capture_engine_); - if (!IsInitialized() && !preview_handler_) { - return; + if (!IsInitialized() || !preview_handler_) { + return S_OK; } // Requests to stop preview. - preview_handler_->StopPreview(capture_engine_.Get()); + return preview_handler_->StopPreview(capture_engine_.Get()); } // Marks preview as paused. @@ -619,16 +652,16 @@ void CaptureControllerImpl::StopPreview() { void CaptureControllerImpl::PausePreview() { assert(capture_controller_listener_); - if (!preview_handler_ && !preview_handler_->IsInitialized()) { + if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnPausePreviewFailed( - "Preview not started"); + CameraResult::kError, "Preview not started"); } if (preview_handler_->PausePreview()) { capture_controller_listener_->OnPausePreviewSucceeded(); } else { capture_controller_listener_->OnPausePreviewFailed( - "Failed to pause preview"); + CameraResult::kError, "Failed to pause preview"); } } @@ -638,16 +671,16 @@ void CaptureControllerImpl::PausePreview() { void CaptureControllerImpl::ResumePreview() { assert(capture_controller_listener_); - if (!preview_handler_ && !preview_handler_->IsInitialized()) { + if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnResumePreviewFailed( - "Preview not started"); + CameraResult::kError, "Preview not started"); } if (preview_handler_->ResumePreview()) { capture_controller_listener_->OnResumePreviewSucceeded(); } else { capture_controller_listener_->OnResumePreviewFailed( - "Failed to pause preview"); + CameraResult::kError, "Failed to pause preview"); } } @@ -675,22 +708,23 @@ void CaptureControllerImpl::OnEvent(IMFMediaEvent* event) { error = Utf8FromUtf16(err.ErrorMessage()); } + CameraResult event_result = GetCameraResult(event_hr); if (extended_type_guid == MF_CAPTURE_ENGINE_ERROR) { - OnCaptureEngineError(event_hr, error); + OnCaptureEngineError(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_INITIALIZED) { - OnCaptureEngineInitialized(SUCCEEDED(event_hr), error); + OnCaptureEngineInitialized(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STARTED) { // Preview is marked as started after first frame is captured. // This is because, CaptureEngine might inform that preview is started // even if error is thrown right after. } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STOPPED) { - OnPreviewStopped(SUCCEEDED(event_hr), error); + OnPreviewStopped(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STARTED) { - OnRecordStarted(SUCCEEDED(event_hr), error); + OnRecordStarted(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STOPPED) { - OnRecordStopped(SUCCEEDED(event_hr), error); + OnRecordStopped(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_PHOTO_TAKEN) { - OnPicture(SUCCEEDED(event_hr), error); + OnPicture(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_CAMERA_STREAM_BLOCKED) { // TODO: Inform capture state to flutter. } else if (extended_type_guid == @@ -701,8 +735,9 @@ void CaptureControllerImpl::OnEvent(IMFMediaEvent* event) { } // Handles Picture event and informs CaptureControllerListener. -void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { - if (success && photo_handler_) { +void CaptureControllerImpl::OnPicture(CameraResult result, + const std::string& error) { + if (result == CameraResult::kSuccess && photo_handler_) { if (capture_controller_listener_) { std::string path = photo_handler_->GetPhotoPath(); capture_controller_listener_->OnTakePictureSucceeded(path); @@ -710,7 +745,7 @@ void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { photo_handler_->OnPhotoTaken(); } else { if (capture_controller_listener_) { - capture_controller_listener_->OnTakePictureFailed(error); + capture_controller_listener_->OnTakePictureFailed(result, error); } // Destroy photo handler on error cases to make sure state is resetted. photo_handler_ = nullptr; @@ -720,8 +755,15 @@ void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { // Handles CaptureEngineInitialized event and informs // CaptureControllerListener. void CaptureControllerImpl::OnCaptureEngineInitialized( - bool success, const std::string& error) { + CameraResult result, const std::string& error) { if (capture_controller_listener_) { + if (result != CameraResult::kSuccess) { + capture_controller_listener_->OnCreateCaptureEngineFailed( + result, "Failed to initialize capture engine"); + ResetCaptureController(); + return; + } + // Create texture handler and register new texture. texture_handler_ = std::make_unique(texture_registrar_); @@ -731,7 +773,7 @@ void CaptureControllerImpl::OnCaptureEngineInitialized( capture_engine_state_ = CaptureEngineState::kInitialized; } else { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create texture_id"); + CameraResult::kError, "Failed to create texture_id"); // Reset state ResetCaptureController(); } @@ -739,10 +781,10 @@ void CaptureControllerImpl::OnCaptureEngineInitialized( } // Handles CaptureEngineError event and informs CaptureControllerListener. -void CaptureControllerImpl::OnCaptureEngineError(HRESULT hr, +void CaptureControllerImpl::OnCaptureEngineError(CameraResult result, const std::string& error) { if (capture_controller_listener_) { - capture_controller_listener_->OnCaptureError(error); + capture_controller_listener_->OnCaptureError(result, error); } // TODO: If MF_CAPTURE_ENGINE_ERROR is returned, @@ -752,9 +794,9 @@ void CaptureControllerImpl::OnCaptureEngineError(HRESULT hr, // Handles PreviewStarted event and informs CaptureControllerListener. // This should be called only after first frame has been received or // in error cases. -void CaptureControllerImpl::OnPreviewStarted(bool success, +void CaptureControllerImpl::OnPreviewStarted(CameraResult result, const std::string& error) { - if (preview_handler_ && success) { + if (preview_handler_ && result == CameraResult::kSuccess) { preview_handler_->OnPreviewStarted(); } else { // Destroy preview handler on error cases to make sure state is resetted. @@ -762,17 +804,18 @@ void CaptureControllerImpl::OnPreviewStarted(bool success, } if (capture_controller_listener_) { - if (success && preview_frame_width_ > 0 && preview_frame_height_ > 0) { + if (result == CameraResult::kSuccess && preview_frame_width_ > 0 && + preview_frame_height_ > 0) { capture_controller_listener_->OnStartPreviewSucceeded( preview_frame_width_, preview_frame_height_); } else { - capture_controller_listener_->OnStartPreviewFailed(error); + capture_controller_listener_->OnStartPreviewFailed(result, error); } } }; // Handles PreviewStopped event. -void CaptureControllerImpl::OnPreviewStopped(bool success, +void CaptureControllerImpl::OnPreviewStopped(CameraResult result, const std::string& error) { // Preview handler is destroyed if preview is stopped as it // does not have any use anymore. @@ -780,16 +823,16 @@ void CaptureControllerImpl::OnPreviewStopped(bool success, }; // Handles RecordStarted event and informs CaptureControllerListener. -void CaptureControllerImpl::OnRecordStarted(bool success, +void CaptureControllerImpl::OnRecordStarted(CameraResult result, const std::string& error) { - if (success && record_handler_) { + if (result == CameraResult::kSuccess && record_handler_) { record_handler_->OnRecordStarted(); if (capture_controller_listener_) { capture_controller_listener_->OnStartRecordSucceeded(); } } else { if (capture_controller_listener_) { - capture_controller_listener_->OnStartRecordFailed(error); + capture_controller_listener_->OnStartRecordFailed(result, error); } // Destroy record handler on error cases to make sure state is resetted. @@ -798,13 +841,13 @@ void CaptureControllerImpl::OnRecordStarted(bool success, }; // Handles RecordStopped event and informs CaptureControllerListener. -void CaptureControllerImpl::OnRecordStopped(bool success, +void CaptureControllerImpl::OnRecordStopped(CameraResult result, const std::string& error) { if (capture_controller_listener_ && record_handler_) { // Always calls OnStopRecord listener methods // to handle separate stop record request for timed records. - if (success) { + if (result == CameraResult::kSuccess) { std::string path = record_handler_->GetRecordPath(); capture_controller_listener_->OnStopRecordSucceeded(path); if (record_handler_->IsTimedRecording()) { @@ -812,14 +855,14 @@ void CaptureControllerImpl::OnRecordStopped(bool success, path, (record_handler_->GetRecordedDuration() / 1000)); } } else { - capture_controller_listener_->OnStopRecordFailed(error); + capture_controller_listener_->OnStopRecordFailed(result, error); if (record_handler_->IsTimedRecording()) { - capture_controller_listener_->OnVideoRecordFailed(error); + capture_controller_listener_->OnVideoRecordFailed(result, error); } } } - if (success && record_handler_) { + if (result == CameraResult::kSuccess && record_handler_) { record_handler_->OnRecordStopped(); } else { // Destroy record handler on error cases to make sure state is resetted. @@ -848,9 +891,9 @@ void CaptureControllerImpl::UpdateCaptureTime(uint64_t capture_time_us) { } if (preview_handler_ && preview_handler_->IsStarting()) { - // Informs that first frame is captured succeffully and preview has + // Informs that first frame is captured successfully and preview has // started. - OnPreviewStarted(true, ""); + OnPreviewStarted(CameraResult::kSuccess, ""); } // Checks if max_video_duration_ms is passed. diff --git a/packages/camera/camera_windows/windows/capture_controller.h b/packages/camera/camera_windows/windows/capture_controller.h index 0b7ab66cabbd..9536be70c50a 100644 --- a/packages/camera/camera_windows/windows/capture_controller.h +++ b/packages/camera/camera_windows/windows/capture_controller.h @@ -207,28 +207,29 @@ class CaptureControllerImpl : public CaptureController, void StopTimedRecord(); // Stops preview. Called internally on camera reset and dispose. - void StopPreview(); + HRESULT StopPreview(); // Handles capture engine initalization event. - void OnCaptureEngineInitialized(bool success, const std::string& error); + void OnCaptureEngineInitialized(CameraResult result, + const std::string& error); // Handles capture engine errors. - void OnCaptureEngineError(HRESULT hr, const std::string& error); + void OnCaptureEngineError(CameraResult result, const std::string& error); // Handles picture events. - void OnPicture(bool success, const std::string& error); + void OnPicture(CameraResult result, const std::string& error); // Handles preview started events. - void OnPreviewStarted(bool success, const std::string& error); + void OnPreviewStarted(CameraResult result, const std::string& error); // Handles preview stopped events. - void OnPreviewStopped(bool success, const std::string& error); + void OnPreviewStopped(CameraResult result, const std::string& error); // Handles record started events. - void OnRecordStarted(bool success, const std::string& error); + void OnRecordStarted(CameraResult result, const std::string& error); // Handles record stopped events. - void OnRecordStopped(bool success, const std::string& error); + void OnRecordStopped(CameraResult result, const std::string& error); bool media_foundation_started_ = false; bool record_audio_ = false; diff --git a/packages/camera/camera_windows/windows/capture_controller_listener.h b/packages/camera/camera_windows/windows/capture_controller_listener.h index 0e713ea7af18..bc7a173925a8 100644 --- a/packages/camera/camera_windows/windows/capture_controller_listener.h +++ b/packages/camera/camera_windows/windows/capture_controller_listener.h @@ -9,6 +9,18 @@ namespace camera_windows { +// Results that can occur when interacting with the camera. +enum class CameraResult { + // Camera operation succeeded. + kSuccess, + + // Camera operation failed. + kError, + + // Camera access permission is denied. + kAccessDenied, +}; + // Interface for classes that receives callbacks on events from the associated // |CaptureController|. class CaptureControllerListener { @@ -22,8 +34,10 @@ class CaptureControllerListener { // Called by CaptureController if initializing the capture engine fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnCreateCaptureEngineFailed(const std::string& error) = 0; + virtual void OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully started preview. // @@ -33,32 +47,40 @@ class CaptureControllerListener { // Called by CaptureController if starting the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStartPreviewFailed(const std::string& error) = 0; + virtual void OnStartPreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully paused preview. virtual void OnPausePreviewSucceeded() = 0; // Called by CaptureController if pausing the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnPausePreviewFailed(const std::string& error) = 0; + virtual void OnPausePreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully resumed preview. virtual void OnResumePreviewSucceeded() = 0; // Called by CaptureController if resuming the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnResumePreviewFailed(const std::string& error) = 0; + virtual void OnResumePreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully started recording. virtual void OnStartRecordSucceeded() = 0; // Called by CaptureController if starting the recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStartRecordFailed(const std::string& error) = 0; + virtual void OnStartRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully stopped recording. // @@ -67,8 +89,10 @@ class CaptureControllerListener { // Called by CaptureController if stopping the recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStopRecordFailed(const std::string& error) = 0; + virtual void OnStopRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully captured picture. // @@ -77,8 +101,10 @@ class CaptureControllerListener { // Called by CaptureController if taking picture fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnTakePictureFailed(const std::string& error) = 0; + virtual void OnTakePictureFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController when timed recording is successfully recorded. // @@ -89,14 +115,18 @@ class CaptureControllerListener { // Called by CaptureController if timed recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnVideoRecordFailed(const std::string& error) = 0; + virtual void OnVideoRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController if capture engine returns error. // For example when camera is disconnected while on use. // + // result: The kind of result. // error: A string describing the error. - virtual void OnCaptureError(const std::string& error) = 0; + virtual void OnCaptureError(CameraResult result, + const std::string& error) = 0; }; } // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/photo_handler.cpp b/packages/camera/camera_windows/windows/photo_handler.cpp index 10df230c2cf2..479f0d3c5ac2 100644 --- a/packages/camera/camera_windows/windows/photo_handler.cpp +++ b/packages/camera/camera_windows/windows/photo_handler.cpp @@ -116,21 +116,23 @@ HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine, return hr; } -bool PhotoHandler::TakePhoto(const std::string& file_path, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type) { +HRESULT PhotoHandler::TakePhoto(const std::string& file_path, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { assert(!file_path.empty()); assert(capture_engine); assert(base_media_type); file_path_ = file_path; - if (FAILED(InitPhotoSink(capture_engine, base_media_type))) { - return false; + HRESULT hr = InitPhotoSink(capture_engine, base_media_type); + if (FAILED(hr)) { + return hr; } photo_state_ = PhotoState::kTakingPhoto; - return SUCCEEDED(capture_engine->TakePhoto()); + + return capture_engine->TakePhoto(); } void PhotoHandler::OnPhotoTaken() { diff --git a/packages/camera/camera_windows/windows/photo_handler.h b/packages/camera/camera_windows/windows/photo_handler.h index ef0d98bfc45f..4d6ddf1a55b8 100644 --- a/packages/camera/camera_windows/windows/photo_handler.h +++ b/packages/camera/camera_windows/windows/photo_handler.h @@ -41,17 +41,17 @@ class PhotoHandler { // to take photo. // // Sets photo state to: kTakingPhoto. - // Returns false if photo cannot be taken. // // capture_engine: A pointer to capture engine instance. // Called to take the photo. // base_media_type: A pointer to base media type used as a base // for the actual photo capture media type. // file_path: A string that hold file path for photo capture. - bool TakePhoto(const std::string& file_path, IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type); + HRESULT TakePhoto(const std::string& file_path, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); - // Set the photo handler recording state to: kIdel. + // Set the photo handler recording state to: kIdle. void OnPhotoTaken(); // Returns true if photo state is kIdle. diff --git a/packages/camera/camera_windows/windows/preview_handler.cpp b/packages/camera/camera_windows/windows/preview_handler.cpp index d7fb2721259c..538754c3e9e2 100644 --- a/packages/camera/camera_windows/windows/preview_handler.cpp +++ b/packages/camera/camera_windows/windows/preview_handler.cpp @@ -113,29 +113,31 @@ HRESULT PreviewHandler::InitPreviewSink( return hr; } -bool PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type, - CaptureEngineListener* sample_callback) { +HRESULT PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback) { assert(capture_engine); assert(base_media_type); - if (FAILED( - InitPreviewSink(capture_engine, base_media_type, sample_callback))) { - return false; + HRESULT hr = + InitPreviewSink(capture_engine, base_media_type, sample_callback); + + if (FAILED(hr)) { + return hr; } preview_state_ = PreviewState::kStarting; - return SUCCEEDED(capture_engine->StartPreview()); + return capture_engine->StartPreview(); } -bool PreviewHandler::StopPreview(IMFCaptureEngine* capture_engine) { +HRESULT PreviewHandler::StopPreview(IMFCaptureEngine* capture_engine) { if (preview_state_ == PreviewState::kStarting || preview_state_ == PreviewState::kRunning || preview_state_ == PreviewState::kPaused) { preview_state_ = PreviewState::kStopping; - return SUCCEEDED(capture_engine->StopPreview()); + return capture_engine->StopPreview(); } - return false; + return E_FAIL; } bool PreviewHandler::PausePreview() { diff --git a/packages/camera/camera_windows/windows/preview_handler.h b/packages/camera/camera_windows/windows/preview_handler.h index 97b85fc28568..311cf5a76c2f 100644 --- a/packages/camera/camera_windows/windows/preview_handler.h +++ b/packages/camera/camera_windows/windows/preview_handler.h @@ -45,7 +45,6 @@ class PreviewHandler { // Initializes preview sink and requests capture engine to start previewing. // Sets preview state to: starting. - // Returns false if recording cannot be started. // // capture_engine: A pointer to capture engine instance. Used to start // the actual recording. @@ -53,16 +52,15 @@ class PreviewHandler { // for the actual video capture media type. // sample_callback: A pointer to capture engine listener. // This is set as sample callback for preview sink. - bool StartPreview(IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type, - CaptureEngineListener* sample_callback); + HRESULT StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback); // Stops existing recording. - // Returns false if recording cannot be stopped. // // capture_engine: A pointer to capture engine instance. Used to stop // the ongoing recording. - bool StopPreview(IMFCaptureEngine* capture_engine); + HRESULT StopPreview(IMFCaptureEngine* capture_engine); // Set the preview handler recording state to: paused. bool PausePreview(); @@ -75,7 +73,7 @@ class PreviewHandler { // Returns true if preview state is running or paused. bool IsInitialized() const { - return preview_state_ == PreviewState::kRunning && + return preview_state_ == PreviewState::kRunning || preview_state_ == PreviewState::kPaused; } diff --git a/packages/camera/camera_windows/windows/record_handler.cpp b/packages/camera/camera_windows/windows/record_handler.cpp index 1cb258e162a5..0f7192533fdd 100644 --- a/packages/camera/camera_windows/windows/record_handler.cpp +++ b/packages/camera/camera_windows/windows/record_handler.cpp @@ -192,10 +192,10 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine, return hr; } -bool RecordHandler::StartRecord(const std::string& file_path, - int64_t max_duration, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type) { +HRESULT RecordHandler::StartRecord(const std::string& file_path, + int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { assert(!file_path.empty()); assert(capture_engine); assert(base_media_type); @@ -206,23 +206,21 @@ bool RecordHandler::StartRecord(const std::string& file_path, recording_start_timestamp_us_ = -1; recording_duration_us_ = 0; - if (FAILED(InitRecordSink(capture_engine, base_media_type))) { - return false; + HRESULT hr = InitRecordSink(capture_engine, base_media_type); + if (FAILED(hr)) { + return hr; } recording_state_ = RecordState::kStarting; - capture_engine->StartRecord(); - - return true; + return capture_engine->StartRecord(); } -bool RecordHandler::StopRecord(IMFCaptureEngine* capture_engine) { +HRESULT RecordHandler::StopRecord(IMFCaptureEngine* capture_engine) { if (recording_state_ == RecordState::kRunning) { recording_state_ = RecordState::kStopping; - HRESULT hr = capture_engine->StopRecord(true, false); - return SUCCEEDED(hr); + return capture_engine->StopRecord(true, false); } - return false; + return E_FAIL; } void RecordHandler::OnRecordStarted() { @@ -238,6 +236,7 @@ void RecordHandler::OnRecordStopped() { recording_duration_us_ = 0; max_video_duration_ms_ = -1; recording_state_ = RecordState::kNotStarted; + type_ = RecordingType::kNone; } } diff --git a/packages/camera/camera_windows/windows/record_handler.h b/packages/camera/camera_windows/windows/record_handler.h index 0daa7f6546a1..0c87bf9cec64 100644 --- a/packages/camera/camera_windows/windows/record_handler.h +++ b/packages/camera/camera_windows/windows/record_handler.h @@ -16,6 +16,8 @@ namespace camera_windows { using Microsoft::WRL::ComPtr; enum class RecordingType { + // Camera is not recording. + kNone, // Recording continues until it is stopped with a separate stop command. kContinuous, // Recording stops automatically after requested record time is passed. @@ -43,7 +45,6 @@ class RecordHandler { // Initializes record sink and requests capture engine to start recording. // // Sets record state to: starting. - // Returns false if recording cannot be started. // // file_path: A string that hold file path for video capture. // max_duration: A int64 value of maximun recording duration. @@ -53,16 +54,15 @@ class RecordHandler { // the actual recording. // base_media_type: A pointer to base media type used as a base // for the actual video capture media type. - bool StartRecord(const std::string& file_path, int64_t max_duration, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type); + HRESULT StartRecord(const std::string& file_path, int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); // Stops existing recording. - // Returns false if recording cannot be stopped. // // capture_engine: A pointer to capture engine instance. Used to stop // the ongoing recording. - bool StopRecord(IMFCaptureEngine* capture_engine); + HRESULT StopRecord(IMFCaptureEngine* capture_engine); // Set the record handler recording state to: running. void OnRecordStarted(); @@ -109,7 +109,7 @@ class RecordHandler { uint64_t recording_duration_us_ = 0; std::string file_path_; RecordState recording_state_ = RecordState::kNotStarted; - RecordingType type_; + RecordingType type_ = RecordingType::kNone; ComPtr record_sink_; }; diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp index 97e3ce113e53..158a2c26c027 100644 --- a/packages/camera/camera_windows/windows/test/camera_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -131,20 +131,37 @@ TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { camera->OnCreateCaptureEngineSucceeded(texture_id); } -TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { +TEST(Camera, CreateCaptureEngineReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); - camera->OnCreateCaptureEngineFailed(error_text); + camera->OnCreateCaptureEngineFailed(CameraResult::kError, error_text); +} + +TEST(Camera, CreateCaptureEngineReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { @@ -169,20 +186,37 @@ TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { camera->OnStartPreviewSucceeded(width, height); } -TEST(Camera, OnStartPreviewFailedReturnsError) { +TEST(Camera, StartPreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); - camera->OnStartPreviewFailed(error_text); + camera->OnStartPreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StartPreviewReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { @@ -199,20 +233,37 @@ TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { camera->OnPausePreviewSucceeded(); } -TEST(Camera, OnPausePreviewFailedReturnsError) { +TEST(Camera, PausePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); - camera->OnPausePreviewFailed(error_text); + camera->OnPausePreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, PausePreviewReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { @@ -230,21 +281,39 @@ TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { camera->OnResumePreviewSucceeded(); } -TEST(Camera, OnResumePreviewFailedReturnsError) { +TEST(Camera, ResumePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kResumePreview, std::move(result)); - camera->OnResumePreviewFailed(error_text); + camera->OnResumePreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, OnResumePreviewPermissionFailureReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStartRecordSucceededReturnsSuccess) { @@ -261,20 +330,37 @@ TEST(Camera, OnStartRecordSucceededReturnsSuccess) { camera->OnStartRecordSucceeded(); } -TEST(Camera, OnStartRecordFailedReturnsError) { +TEST(Camera, StartRecordReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); - camera->OnStartRecordFailed(error_text); + camera->OnStartRecordFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StartRecordReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStopRecordSucceededReturnsSuccess) { @@ -283,7 +369,7 @@ TEST(Camera, OnStopRecordSucceededReturnsSuccess) { std::unique_ptr result = std::make_unique(); - std::string file_path = "C:\temp\filename.mp4"; + const std::string file_path = "C:\temp\filename.mp4"; EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); @@ -293,20 +379,37 @@ TEST(Camera, OnStopRecordSucceededReturnsSuccess) { camera->OnStopRecordSucceeded(file_path); } -TEST(Camera, OnStopRecordFailedReturnsError) { +TEST(Camera, StopRecordReportsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StopRecordReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); - camera->OnStopRecordFailed(error_text); + camera->OnStopRecordFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnTakePictureSucceededReturnsSuccess) { @@ -315,7 +418,7 @@ TEST(Camera, OnTakePictureSucceededReturnsSuccess) { std::unique_ptr result = std::make_unique(); - std::string file_path = "C:\temp\filename.jpeg"; + const std::string file_path = "C:\\temp\\filename.jpeg"; EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); @@ -325,20 +428,37 @@ TEST(Camera, OnTakePictureSucceededReturnsSuccess) { camera->OnTakePictureSucceeded(file_path); } -TEST(Camera, OnTakePictureFailedReturnsError) { +TEST(Camera, TakePictureReportsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureFailed(CameraResult::kError, error_text); +} + +TEST(Camera, TakePictureReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); - camera->OnTakePictureFailed(error_text); + camera->OnTakePictureFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { @@ -350,12 +470,12 @@ TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { std::unique_ptr binary_messenger = std::make_unique(); - std::string file_path = "C:\temp\filename.mp4"; - int64_t camera_id = 12345; + const std::string file_path = "C:\\temp\\filename.mp4"; + const int64_t camera_id = 12345; std::string camera_channel = std::string("plugins.flutter.io/camera_windows/camera") + std::to_string(camera_id); - int64_t video_duration = 1000000; + const int64_t video_duration = 1000000; EXPECT_CALL(*capture_controller_factory, CreateCaptureController) .Times(1) diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 083f823222b6..4662d3340456 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -70,35 +70,10 @@ void MockInitCaptureController(CaptureControllerImpl* capture_controller, engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_INITIALIZED); } -void MockStartPreview(CaptureControllerImpl* capture_controller, - MockCaptureSource* capture_source, - MockCapturePreviewSink* preview_sink, - MockTextureRegistrar* texture_registrar, - MockCaptureEngine* engine, MockCamera* camera, - std::unique_ptr mock_source_buffer, - uint32_t mock_source_buffer_size, - uint32_t mock_preview_width, uint32_t mock_preview_height, - int64_t mock_texture_id) { - EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) - .Times(1) - .WillOnce([src_sink = preview_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, - IMFCaptureSink** target_sink) { - *target_sink = src_sink; - src_sink->AddRef(); - return S_OK; - }); - - EXPECT_CALL(*preview_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*preview_sink, AddStream).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*preview_sink, SetSampleCallback) - .Times(1) - .WillOnce([sink = preview_sink]( - DWORD dwStreamSinkIndex, - IMFCaptureEngineOnSampleCallback* pCallback) -> HRESULT { - sink->sample_callback_ = pCallback; - return S_OK; - }); - +void MockAvailableMediaTypes(MockCaptureEngine* engine, + MockCaptureSource* capture_source, + uint32_t mock_preview_width, + uint32_t mock_preview_height) { EXPECT_CALL(*engine, GetSource) .Times(1) .WillOnce( @@ -142,6 +117,39 @@ void MockStartPreview(CaptureControllerImpl* capture_controller, (*media_type)->AddRef(); return S_OK; }); +} + +void MockStartPreview(CaptureControllerImpl* capture_controller, + MockCapturePreviewSink* preview_sink, + MockTextureRegistrar* texture_registrar, + MockCaptureEngine* engine, MockCamera* camera, + std::unique_ptr mock_source_buffer, + uint32_t mock_source_buffer_size, + uint32_t mock_preview_width, uint32_t mock_preview_height, + int64_t mock_texture_id) { + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce([src_sink = preview_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*preview_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, SetSampleCallback) + .Times(1) + .WillOnce([sink = preview_sink]( + DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback) -> HRESULT { + sink->sample_callback_ = pCallback; + return S_OK; + }); + + ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine, capture_source.Get(), mock_preview_width, + mock_preview_height); EXPECT_CALL(*engine, StartPreview()).Times(1).WillOnce(Return(S_OK)); @@ -169,6 +177,21 @@ void MockStartPreview(CaptureControllerImpl* capture_controller, mock_source_buffer_size); } +void MockPhotoSink(MockCaptureEngine* engine, + MockCapturePhotoSink* photo_sink) { + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, _)) + .Times(1) + .WillOnce([src_sink = photo_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + EXPECT_CALL(*photo_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*photo_sink, AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*photo_sink, SetOutputFileName).Times(1).WillOnce(Return(S_OK)); +} + void MockRecordStart(CaptureControllerImpl* capture_controller, MockCaptureEngine* engine, MockCaptureRecordSink* record_sink, MockCamera* camera, @@ -204,7 +227,7 @@ TEST(CaptureController, std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Init capture controller with mocks and tests MockInitCaptureController(capture_controller.get(), texture_registrar.get(), @@ -225,7 +248,7 @@ TEST(CaptureController, InitCaptureEngineCanOnlyBeCalledOnce) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Init capture controller once with mocks and tests MockInitCaptureController(capture_controller.get(), texture_registrar.get(), @@ -264,16 +287,59 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { capture_controller->SetAudioSource( reinterpret_cast(audio_source.Get())); + // Cause initialization to fail + EXPECT_CALL(*engine.Get(), Initialize).Times(1).WillOnce(Return(E_FAIL)); + EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + EXPECT_CALL(*camera, + OnCreateCaptureEngineFailed(Eq(CameraResult::kError), + Eq("Failed to create camera"))) + .Times(1); + + bool result = capture_controller->InitCaptureDevice( + texture_registrar.get(), MOCK_DEVICE_ID, true, ResolutionPreset::kAuto); + + EXPECT_FALSE(result); + EXPECT_FALSE(engine->initialized_); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, InitCaptureEngineReportsAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + ComPtr video_source = new MockMediaSource(); + ComPtr audio_source = new MockMediaSource(); + + capture_controller->SetCaptureEngine( + reinterpret_cast(engine.Get())); + capture_controller->SetVideoSource( + reinterpret_cast(video_source.Get())); + capture_controller->SetAudioSource( + reinterpret_cast(audio_source.Get())); + // Cause initialization to fail EXPECT_CALL(*engine.Get(), Initialize) .Times(1) .WillOnce(Return(E_ACCESSDENIED)); + EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, - OnCreateCaptureEngineFailed(Eq("Failed to create camera"))) + OnCreateCaptureEngineFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to create camera"))) .Times(1); bool result = capture_controller->InitCaptureDevice( @@ -288,6 +354,118 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { engine = nullptr; } +TEST(CaptureController, ReportsInitializedErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed( + Eq(CameraResult::kError), + Eq("Failed to initialize capture engine"))) + .Times(1); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + // Send initialization failed event + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_INITIALIZED); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, ReportsInitializedAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed( + Eq(CameraResult::kAccessDenied), + Eq("Failed to initialize capture engine"))) + .Times(1); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + // Send initialization failed event + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_INITIALIZED); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, ReportsCaptureEngineErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*(camera.get()), + OnCaptureError(Eq(CameraResult::kError), Eq("Unspecified error"))) + .Times(1); + + // Send error event. + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_ERROR); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, ReportsCaptureEngineAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*(camera.get()), OnCaptureError(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + // Send error event. + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_ERROR); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + TEST(CaptureController, StartPreviewStartsProcessingSamples) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -297,14 +475,13 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); ComPtr preview_sink = new MockCapturePreviewSink(); - ComPtr capture_source = new MockCaptureSource(); // Let's keep these small for mock texture data. Two pixels should be // enough. @@ -332,11 +509,10 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { } // Start preview and run preview tests - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), - mock_texture_data_size, mock_preview_width, - mock_preview_height, mock_texture_id); + MockStartPreview(capture_controller.get(), preview_sink.Get(), + texture_registrar.get(), engine.Get(), camera.get(), + std::move(mock_source_buffer), mock_texture_data_size, + mock_preview_width, mock_preview_height, mock_texture_id); // Test texture processing EXPECT_TRUE(texture_registrar->texture_); @@ -377,7 +553,7 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { texture_registrar = nullptr; } -TEST(CaptureController, StartRecordSuccess) { +TEST(CaptureController, ReportsStartPreviewError) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -386,43 +562,39 @@ TEST(CaptureController, StartRecordSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); - std::unique_ptr mock_source_buffer = - std::make_unique(0); - - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + // Cause start preview to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce(Return(E_FAIL)); - // Start record - ComPtr record_sink = new MockCaptureRecordSink(); - std::string mock_path_to_video = "mock_path_to_video"; - MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), - camera.get(), mock_path_to_video); + EXPECT_CALL(*engine.Get(), StartPreview).Times(0); + EXPECT_CALL(*engine.Get(), StopPreview).Times(0); + EXPECT_CALL(*camera, OnStartPreviewSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartPreviewFailed(Eq(CameraResult::kError), + Eq("Failed to start video preview"))) + .Times(1); - // Called by destructor - EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) - .Times(1) - .WillOnce(Return(S_OK)); + capture_controller->StartPreview(); capture_controller = nullptr; - texture_registrar = nullptr; engine = nullptr; camera = nullptr; - record_sink = nullptr; + texture_registrar = nullptr; } -TEST(CaptureController, StopRecordSuccess) { +// TODO(loic-sharma): Test duplicate calls to start preview. + +TEST(CaptureController, IgnoresStartPreviewErrorEvent) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -431,48 +603,25 @@ TEST(CaptureController, StopRecordSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); - ComPtr capture_source = new MockCaptureSource(); - - std::unique_ptr mock_source_buffer = - std::make_unique(0); - - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); - - // Start record - ComPtr record_sink = new MockCaptureRecordSink(); - std::string mock_path_to_video = "mock_path_to_video"; - MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), - camera.get(), mock_path_to_video); - - // Request to stop record - EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) - .Times(1) - .WillOnce(Return(S_OK)); - capture_controller->StopRecord(); + EXPECT_CALL(*camera, OnStartPreviewFailed).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); - // OnStopRecordSucceeded should be called with mocked file path - EXPECT_CALL(*camera, OnStopRecordSucceeded(Eq(mock_path_to_video))).Times(1); - engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_RECORD_STOPPED); + // Send a start preview error event + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_PREVIEW_STARTED); capture_controller = nullptr; + camera = nullptr; texture_registrar = nullptr; engine = nullptr; - camera = nullptr; - record_sink = nullptr; } -TEST(CaptureController, TakePictureSuccess) { +TEST(CaptureController, ReportsStartPreviewAccessDenied) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -481,43 +630,526 @@ TEST(CaptureController, TakePictureSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); - std::unique_ptr mock_source_buffer = - std::make_unique(0); + // Cause start preview to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + EXPECT_CALL(*engine.Get(), StartPreview).Times(0); + EXPECT_CALL(*engine.Get(), StopPreview).Times(0); + EXPECT_CALL(*camera, OnStartPreviewSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartPreviewFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to start video preview"))) + .Times(1); - // Init photo sink tests - ComPtr photo_sink = new MockCapturePhotoSink(); - EXPECT_CALL(*(engine.Get()), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, _)) - .Times(1) - .WillOnce( - [src_sink = photo_sink.Get()](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, - IMFCaptureSink** target_sink) { - *target_sink = src_sink; - src_sink->AddRef(); - return S_OK; - }); - EXPECT_CALL(*(photo_sink.Get()), RemoveAllStreams) + capture_controller->StartPreview(); + + capture_controller = nullptr; + engine = nullptr; + camera = nullptr; + texture_registrar = nullptr; +} + +TEST(CaptureController, StartRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Called by destructor + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(S_OK)); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStartRecordError) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Cause start record to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*engine.Get(), StartRecord).Times(0); + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartRecordFailed(Eq(CameraResult::kError), + Eq("Failed to start video recording"))) + .Times(1); + + capture_controller->StartRecord("mock_path", -1); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ReportsStartRecordAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Cause start record to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*engine.Get(), StartRecord).Times(0); + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to start video recording"))) + .Times(1); + + capture_controller->StartRecord("mock_path", -1); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ReportsStartRecordErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + + EXPECT_CALL(*engine.Get(), StartRecord()).Times(1).WillOnce(Return(S_OK)); + + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce([src_sink = record_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink.Get(); + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*record_sink.Get(), RemoveAllStreams) .Times(1) .WillOnce(Return(S_OK)); - EXPECT_CALL(*(photo_sink.Get()), AddStream).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*(photo_sink.Get()), SetOutputFileName) + EXPECT_CALL(*record_sink.Get(), AddStream) + .Times(2) + .WillRepeatedly(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), SetOutputFileName) .Times(1) .WillOnce(Return(S_OK)); + capture_controller->StartRecord(mock_path_to_video, -1); + + // Send a start record failed event + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStartRecordFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STARTED); + + // Destructor shouldn't attempt to stop the recording that failed to start. + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStartRecordAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + + EXPECT_CALL(*engine.Get(), StartRecord()).Times(1).WillOnce(Return(S_OK)); + + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce([src_sink = record_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink.Get(); + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*record_sink.Get(), RemoveAllStreams) + .Times(1) + .WillOnce(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), AddStream) + .Times(2) + .WillRepeatedly(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), SetOutputFileName) + .Times(1) + .WillOnce(Return(S_OK)); + + // Send a start record failed event + capture_controller->StartRecord(mock_path_to_video, -1); + + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStartRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_RECORD_STARTED); + + // Destructor shouldn't attempt to stop the recording that failed to start. + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, StopRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Request to stop record + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(S_OK)); + capture_controller->StopRecord(); + + // OnStopRecordSucceeded should be called with mocked file path + EXPECT_CALL(*camera, OnStopRecordSucceeded(Eq(mock_path_to_video))).Times(1); + EXPECT_CALL(*camera, OnStopRecordFailed).Times(0); + + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordError) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), "mock_path_to_video"); + + // Cause stop record to fail + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kError), + Eq("Failed to stop video recording"))) + .Times(1); + + capture_controller->StopRecord(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), "mock_path_to_video"); + + // Cause stop record to fail + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to stop video recording"))) + .Times(1); + + capture_controller->StopRecord(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Send a stop record failure event + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Send a stop record failure event + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, TakePictureSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + // Request photo std::string mock_path_to_photo = "mock_path_to_photo"; EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); @@ -525,6 +1157,7 @@ TEST(CaptureController, TakePictureSuccess) { // OnTakePictureSucceeded should be called with mocked file path EXPECT_CALL(*camera, OnTakePictureSucceeded(Eq(mock_path_to_photo))).Times(1); + EXPECT_CALL(*camera, OnTakePictureFailed).Times(0); engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_PHOTO_TAKEN); capture_controller = nullptr; @@ -534,6 +1167,182 @@ TEST(CaptureController, TakePictureSuccess) { photo_sink = nullptr; } +TEST(CaptureController, ReportsTakePictureError) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Cause take picture to fail + EXPECT_CALL(*(engine.Get()), TakePhoto).Times(1).WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kError), + Eq("Failed to take photo"))) + .Times(1); + + capture_controller->TakePicture("mock_path_to_photo"); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, ReportsTakePictureAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Cause take picture to fail. + EXPECT_CALL(*(engine.Get()), TakePhoto) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to take photo"))) + .Times(1); + + capture_controller->TakePicture("mock_path_to_photo"); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, ReportsPhotoTakenErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Request photo + std::string mock_path_to_photo = "mock_path_to_photo"; + EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); + capture_controller->TakePicture(mock_path_to_photo); + + // Send take picture failed event + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_PHOTO_TAKEN); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, ReportsPhotoTakenAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Request photo + std::string mock_path_to_photo = "mock_path_to_photo"; + EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); + capture_controller->TakePicture(mock_path_to_photo); + + // Send take picture failed event + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_PHOTO_TAKEN); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + TEST(CaptureController, PauseResumePreviewSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -543,23 +1352,21 @@ TEST(CaptureController, PauseResumePreviewSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); ComPtr preview_sink = new MockCapturePreviewSink(); - ComPtr capture_source = new MockCaptureSource(); std::unique_ptr mock_source_buffer = std::make_unique(0); // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + MockStartPreview(capture_controller.get(), preview_sink.Get(), + texture_registrar.get(), engine.Get(), camera.get(), + std::move(mock_source_buffer), 0, 1, 1, mock_texture_id); EXPECT_CALL(*camera, OnPausePreviewSucceeded()).Times(1); capture_controller->PausePreview(); @@ -573,5 +1380,59 @@ TEST(CaptureController, PauseResumePreviewSuccess) { camera = nullptr; } +TEST(CaptureController, PausePreviewFailsIfPreviewNotStarted) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + // Pause preview fails if not started + EXPECT_CALL(*camera, OnPausePreviewFailed(Eq(CameraResult::kError), + Eq("Preview not started"))) + .Times(1); + + capture_controller->PausePreview(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ResumePreviewFailsIfPreviewNotStarted) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + // Resume preview fails if not started. + EXPECT_CALL(*camera, OnResumePreviewFailed(Eq(CameraResult::kError), + Eq("Preview not started"))) + .Times(1); + + capture_controller->ResumePreview(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + } // namespace test } // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 53101f5e6c17..678b75899bc2 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -134,41 +134,43 @@ class MockCamera : public Camera { (override)); MOCK_METHOD(std::unique_ptr>, GetPendingResultByType, (PendingResultType type)); - MOCK_METHOD(void, OnCreateCaptureEngineFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnCreateCaptureEngineFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStartPreviewSucceeded, (int32_t width, int32_t height), (override)); - MOCK_METHOD(void, OnStartPreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnStartPreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnResumePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnResumePreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnResumePreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnPausePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnPausePreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnPausePreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStartRecordSucceeded, (), (override)); - MOCK_METHOD(void, OnStartRecordFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnStartRecordFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStopRecordSucceeded, (const std::string& file_path), (override)); - MOCK_METHOD(void, OnStopRecordFailed, (const std::string& error), (override)); + MOCK_METHOD(void, OnStopRecordFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnTakePictureSucceeded, (const std::string& file_path), (override)); - MOCK_METHOD(void, OnTakePictureFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnTakePictureFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnVideoRecordSucceeded, (const std::string& file_path, int64_t video_duration), (override)); - MOCK_METHOD(void, OnVideoRecordFailed, (const std::string& error), - (override)); - MOCK_METHOD(void, OnCaptureError, (const std::string& error), (override)); + MOCK_METHOD(void, OnVideoRecordFailed, + (CameraResult result, const std::string& error), (override)); + MOCK_METHOD(void, OnCaptureError, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 120b7b00ed15..fda959a21579 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.1 + +* Exports new types added for `getMultiImageWithOptions` in 2.6.0. + ## 2.6.0 * Deprecates `getMultiImage` in favor of a new method `getMultiImageWithOptions`. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index d215fa2684ee..80d33807b70d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index a2618d5b419c..f704025f581b 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:cross_file/cross_file.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index dad86c5d1ba1..fbe12e8e825a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -3,9 +3,11 @@ // found in the LICENSE file. export 'camera_device.dart'; +export 'image_options.dart'; export 'image_picker_options.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; +export 'multi_image_picker_options.dart'; export 'picked_file/picked_file.dart'; export 'retrieve_type.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 50d84f81d888..567914316c1a 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.6.0 +version: 2.6.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 27d7016d8143..1ddddee079b7 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -4,11 +4,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; -import 'package:image_picker_platform_interface/src/types/image_options.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 1aae73d7393e..975d171889cc 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +* Replaces `USE_FINGERPRINT` permission with `USE_BIOMETRIC` in README and example project. + ## 2.1.0 * Adds Windows support. diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 3077f8e60a1f..a68692ea940a 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -235,12 +235,12 @@ Note that `local_auth` requires the use of a `FragmentActivity` instead of an ### Permissions Update your project's `AndroidManifest.xml` file to include the -`USE_FINGERPRINT` permissions: +`USE_BIOMETRIC` permissions: ```xml - + ``` diff --git a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml index 8c091772107a..4acc4eb87ed6 100644 --- a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="io.flutter.plugins.localauthexample"> - + =2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index f6d9da2c504c..6a308ee95285 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.9 + +* Updates androidx.fragment version to 1.5.1. + ## 1.0.8 * Removes usages of `FingerprintManager` and other `BiometricManager` deprecated method usages. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index eb9be4ab5f4b..8d2f40245f2d 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -51,7 +51,7 @@ android { dependencies { api "androidx.core:core:1.8.0" api "androidx.biometric:biometric:1.1.0" - api "androidx.fragment:fragment:1.3.2" + api "androidx.fragment:fragment:1.5.1" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.6.1' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml index 8c091772107a..4acc4eb87ed6 100644 --- a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="io.flutter.plugins.localauthexample"> - + =2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 8c0c18e820e3..91f7389ff251 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -130,7 +130,7 @@ class DefaultLinkDelegate extends StatelessWidget { ); } else { FlutterError.reportError(FlutterErrorDetails( - exception: 'Could not launch link ${url.toString()}', + exception: 'Could not launch link $url', stack: StackTrace.current, library: 'url_launcher', context: ErrorDescription('during launching a link'), diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 5706d653a09a..dc9850e195dd 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.3 + +* Updates the Dart InstanceManager to take a listener for when an object is garbage collected. + See https://github.com/flutter/flutter/issues/107199. + ## 2.9.2 * Updates the Java InstanceManager to take a listener for when an object is garbage collected. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 005e2818a9f0..f2e305fe85c5 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -28,6 +28,11 @@ abstract class JavaObject with Copyable { /// This should only be used by subclasses created by this library or to /// create copies. JavaObject.detached(); + + /// Global instance of [InstanceManager]. + static final InstanceManager globalInstanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); } /// An Android View that displays web pages. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index b1268f4f9cb0..c3e2096bbe0f 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -88,28 +88,27 @@ class WebViewHostApiImpl extends WebViewHostApi { WebViewHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. - Future createFromInstance(WebView instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.useHybridComposition); - } + Future createFromInstance(WebView instance) { + return create( + instanceManager.addDartCreatedInstance(instance), + instance.useHybridComposition, + ); } /// Helper method to convert instances ids to objects. Future disposeFromInstance(WebView instance) async { - final int? instanceId = instanceManager.getInstanceId(instance); + final int? instanceId = instanceManager.getIdentifier(instance); if (instanceId != null) { + instanceManager.remove(instanceId); await dispose(instanceId); } - instanceManager.removeInstance(instance); } /// Helper method to convert the instances ids to objects. @@ -120,7 +119,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String? encoding, ) { return loadData( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, data, mimeType, encoding, @@ -137,7 +136,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String? historyUrl, ) { return loadDataWithBaseUrl( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, baseUrl, data, mimeType, @@ -152,7 +151,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String url, Map headers, ) { - return loadUrl(instanceManager.getInstanceId(instance)!, url, headers); + return loadUrl(instanceManager.getIdentifier(instance)!, url, headers); } /// Helper method to convert instances ids to objects. @@ -161,43 +160,43 @@ class WebViewHostApiImpl extends WebViewHostApi { String url, Uint8List data, ) { - return postUrl(instanceManager.getInstanceId(instance)!, url, data); + return postUrl(instanceManager.getIdentifier(instance)!, url, data); } /// Helper method to convert instances ids to objects. Future getUrlFromInstance(WebView instance) { - return getUrl(instanceManager.getInstanceId(instance)!); + return getUrl(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future canGoBackFromInstance(WebView instance) { - return canGoBack(instanceManager.getInstanceId(instance)!); + return canGoBack(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future canGoForwardFromInstance(WebView instance) { - return canGoForward(instanceManager.getInstanceId(instance)!); + return canGoForward(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future goBackFromInstance(WebView instance) { - return goBack(instanceManager.getInstanceId(instance)!); + return goBack(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future goForwardFromInstance(WebView instance) { - return goForward(instanceManager.getInstanceId(instance)!); + return goForward(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future reloadFromInstance(WebView instance) { - return reload(instanceManager.getInstanceId(instance)!); + return reload(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future clearCacheFromInstance(WebView instance, bool includeDiskFiles) { return clearCache( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, includeDiskFiles, ); } @@ -208,34 +207,34 @@ class WebViewHostApiImpl extends WebViewHostApi { String javascriptString, ) { return evaluateJavascript( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, javascriptString, ); } /// Helper method to convert instances ids to objects. Future getTitleFromInstance(WebView instance) { - return getTitle(instanceManager.getInstanceId(instance)!); + return getTitle(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future scrollToFromInstance(WebView instance, int x, int y) { - return scrollTo(instanceManager.getInstanceId(instance)!, x, y); + return scrollTo(instanceManager.getIdentifier(instance)!, x, y); } /// Helper method to convert instances ids to objects. Future scrollByFromInstance(WebView instance, int x, int y) { - return scrollBy(instanceManager.getInstanceId(instance)!, x, y); + return scrollBy(instanceManager.getIdentifier(instance)!, x, y); } /// Helper method to convert instances ids to objects. Future getScrollXFromInstance(WebView instance) { - return getScrollX(instanceManager.getInstanceId(instance)!); + return getScrollX(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future getScrollYFromInstance(WebView instance) { - return getScrollY(instanceManager.getInstanceId(instance)!); + return getScrollY(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. @@ -244,8 +243,8 @@ class WebViewHostApiImpl extends WebViewHostApi { WebViewClient webViewClient, ) { return setWebViewClient( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(webViewClient)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(webViewClient)!, ); } @@ -255,8 +254,8 @@ class WebViewHostApiImpl extends WebViewHostApi { JavaScriptChannel javaScriptChannel, ) { return addJavaScriptChannel( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(javaScriptChannel)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(javaScriptChannel)!, ); } @@ -266,8 +265,8 @@ class WebViewHostApiImpl extends WebViewHostApi { JavaScriptChannel javaScriptChannel, ) { return removeJavaScriptChannel( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(javaScriptChannel)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(javaScriptChannel)!, ); } @@ -277,8 +276,8 @@ class WebViewHostApiImpl extends WebViewHostApi { DownloadListener? listener, ) { return setDownloadListener( - instanceManager.getInstanceId(instance)!, - listener != null ? instanceManager.getInstanceId(listener) : null, + instanceManager.getIdentifier(instance)!, + listener != null ? instanceManager.getIdentifier(listener) : null, ); } @@ -288,14 +287,14 @@ class WebViewHostApiImpl extends WebViewHostApi { WebChromeClient? client, ) { return setWebChromeClient( - instanceManager.getInstanceId(instance)!, - client != null ? instanceManager.getInstanceId(client) : null, + instanceManager.getIdentifier(instance)!, + client != null ? instanceManager.getIdentifier(client) : null, ); } /// Helper method to convert instances ids to objects. Future setBackgroundColorFromInstance(WebView instance, int color) { - return setBackgroundColor(instanceManager.getInstanceId(instance)!, color); + return setBackgroundColor(instanceManager.getIdentifier(instance)!, color); } } @@ -305,28 +304,25 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettingsHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. - Future createFromInstance(WebSettings instance, WebView webView) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create( - instanceId, - instanceManager.getInstanceId(webView)!, - ); - } + Future createFromInstance(WebSettings instance, WebView webView) { + return create( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(webView)!, + ); } /// Helper method to convert instances ids to objects. Future disposeFromInstance(WebSettings instance) async { - final int? instanceId = instanceManager.removeInstance(instance); + final int? instanceId = instanceManager.getIdentifier(instance); if (instanceId != null) { + instanceManager.remove(instanceId); return dispose(instanceId); } } @@ -336,7 +332,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool flag, ) { - return setDomStorageEnabled(instanceManager.getInstanceId(instance)!, flag); + return setDomStorageEnabled(instanceManager.getIdentifier(instance)!, flag); } /// Helper method to convert instances ids to objects. @@ -345,7 +341,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool flag, ) { return setJavaScriptCanOpenWindowsAutomatically( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, flag, ); } @@ -356,7 +352,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool support, ) { return setSupportMultipleWindows( - instanceManager.getInstanceId(instance)!, support); + instanceManager.getIdentifier(instance)!, support); } /// Helper method to convert instances ids to objects. @@ -365,7 +361,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool flag, ) { return setJavaScriptEnabled( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, flag, ); } @@ -376,7 +372,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { String? userAgentString, ) { return setUserAgentString( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, userAgentString, ); } @@ -387,7 +383,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool require, ) { return setMediaPlaybackRequiresUserGesture( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, require, ); } @@ -397,7 +393,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool support, ) { - return setSupportZoom(instanceManager.getInstanceId(instance)!, support); + return setSupportZoom(instanceManager.getIdentifier(instance)!, support); } /// Helper method to convert instances ids to objects. @@ -406,7 +402,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool overview, ) { return setLoadWithOverviewMode( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, overview, ); } @@ -416,7 +412,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool use, ) { - return setUseWideViewPort(instanceManager.getInstanceId(instance)!, use); + return setUseWideViewPort(instanceManager.getIdentifier(instance)!, use); } /// Helper method to convert instances ids to objects. @@ -425,7 +421,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setDisplayZoomControls( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -436,7 +432,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setBuiltInZoomControls( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -447,7 +443,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setAllowFileAccess( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -459,18 +455,20 @@ class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi { JavaScriptChannelHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(JavaScriptChannel instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.channelName); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + await create( + identifier, + instance.channelName, + ); } } } @@ -478,22 +476,21 @@ class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi { /// Flutter api implementation for [JavaScriptChannel]. class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi { /// Constructs a [JavaScriptChannelFlutterApiImpl]. - JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void postMessage(int instanceId, String message) { - final JavaScriptChannel? instance = - instanceManager.getInstance(instanceId) as JavaScriptChannel?; + final JavaScriptChannel? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as JavaScriptChannel?; assert( instance != null, 'InstanceManager does not contain an JavaScriptChannel with instanceId: $instanceId', @@ -508,18 +505,17 @@ class WebViewClientHostApiImpl extends WebViewClientHostApi { WebViewClientHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(WebViewClient instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.shouldOverrideUrlLoading); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier, instance.shouldOverrideUrlLoading); } } } @@ -527,24 +523,23 @@ class WebViewClientHostApiImpl extends WebViewClientHostApi { /// Flutter api implementation for [WebViewClient]. class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { /// Constructs a [WebViewClientFlutterApiImpl]. - WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void onPageFinished(int instanceId, int webViewInstanceId, String url) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -558,10 +553,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { @override void onPageStarted(int instanceId, int webViewInstanceId, String url) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -581,10 +576,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { String description, String failingUrl, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -609,10 +604,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { WebResourceRequestData request, WebResourceErrorData error, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -634,10 +629,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { int webViewInstanceId, WebResourceRequestData request, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -655,10 +650,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { int webViewInstanceId, String url, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', @@ -677,18 +672,17 @@ class DownloadListenerHostApiImpl extends DownloadListenerHostApi { DownloadListenerHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(DownloadListener instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier); } } } @@ -696,16 +690,15 @@ class DownloadListenerHostApiImpl extends DownloadListenerHostApi { /// Flutter api implementation for [DownloadListener]. class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi { /// Constructs a [DownloadListenerFlutterApiImpl]. - DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override @@ -717,8 +710,8 @@ class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi { String mimetype, int contentLength, ) { - final DownloadListener? instance = - instanceManager.getInstance(instanceId) as DownloadListener?; + final DownloadListener? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as DownloadListener?; assert( instance != null, 'InstanceManager does not contain an DownloadListener with instanceId: $instanceId', @@ -739,21 +732,23 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi { WebChromeClientHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance( WebChromeClient instance, WebViewClient webViewClient, ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instanceManager.getInstanceId(webViewClient)!); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create( + identifier, + instanceManager.getIdentifier(webViewClient)!, + ); } } } @@ -761,24 +756,23 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi { /// Flutter api implementation for [DownloadListener]. class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { /// Constructs a [DownloadListenerFlutterApiImpl]. - WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void onProgressChanged(int instanceId, int webViewInstanceId, int progress) { - final WebChromeClient? instance = - instanceManager.getInstance(instanceId) as WebChromeClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebChromeClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebChromeClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebChromeClient with instanceId: $instanceId', @@ -797,23 +791,22 @@ class WebStorageHostApiImpl extends WebStorageHostApi { WebStorageHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(WebStorage instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier); } } /// Helper method to convert instances ids to objects. Future deleteAllDataFromInstance(WebStorage instance) { - return deleteAllData(instanceManager.getInstanceId(instance)!); + return deleteAllData(instanceManager.getIdentifier(instance)!); } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart index 531fffa50e81..5892823aabd3 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart @@ -23,51 +23,179 @@ mixin Copyable { Copyable copy(); } -/// Maintains instances stored to communicate with java objects. +/// Maintains instances used to communicate with the native objects they +/// represent. +/// +/// Added instances are stored as weak references and their copies are stored +/// as strong references to maintain access to their variables and callback +/// methods. Both are stored with the same identifier. +/// +/// When a weak referenced instance becomes inaccessible, +/// [onWeakReferenceRemoved] is called with its associated identifier. +/// +/// If an instance is retrieved and has the possibility to be used, +/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference +/// is added as a weak reference with the same identifier. This prevents a +/// scenario where the weak referenced instance was released and then later +/// returned by the host platform. class InstanceManager { - final Map _instanceIdsToInstances = {}; - final Map _instancesToInstanceIds = {}; + /// Constructs an [InstanceManager]. + InstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } - int _nextInstanceId = 0; + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously by the host platform. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + static const int _maxDartCreatedIdentifier = 65536; + + // Expando is used because it doesn't prevent its keys from becoming + // inaccessible. This allows the manager to efficiently retrieve an identifier + // of an instance without holding a strong reference to that instance. + // + // It also doesn't use `==` to search for identifiers, which would lead to an + // infinite loop when comparing an object to its copy. (i.e. which was caused + // by calling instanceManager.getIdentifier() inside of `==` while this was a + // HashMap). + final Expando _identifiers = Expando(); + final Map> _weakInstances = + >{}; + final Map _strongInstances = {}; + late final Finalizer _finalizer; + int _nextIdentifier = 0; + + /// Called when a weak referenced instance is removed by [removeWeakReference] + /// or becomes inaccessible. + late final void Function(int) onWeakReferenceRemoved; + + /// Adds a new instance that was instantiated by Dart. + /// + /// In other words, Dart wants to add a new instance that will represent + /// an object that will be instantiated on the host platform. + /// + /// Throws assertion error if the instance has already been added. + /// + /// Returns the randomly generated id of the [instance] added. + int addDartCreatedInstance(Copyable instance) { + assert(getIdentifier(instance) == null); - /// Global instance of [InstanceManager]. - static final InstanceManager instance = InstanceManager(); + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; + } - /// Attempt to add a new instance. + /// Removes the instance, if present, and call [onWeakReferenceRemoved] with + /// its identifier. + /// + /// Returns the identifier associated with the removed instance. Otherwise, + /// `null` if the instance was not found in this manager. /// - /// Returns new if [instance] has already been added. Otherwise, it is added - /// with a new instance id. - int? tryAddInstance(Object instance) { - if (_instancesToInstanceIds.containsKey(instance)) { + /// This does not remove the the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(Copyable instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { return null; } - final int instanceId = _nextInstanceId++; - _instancesToInstanceIds[instance] = instanceId; - _instanceIdsToInstances[instanceId] = instance; - return instanceId; + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; } - /// Remove the instance from the manager. + /// Removes [identifier] and its associated strongly referenced instance, if + /// present, from the manager. + /// + /// Returns the strong referenced instance associated with [identifier] before + /// it was removed. Returns `null` if [identifier] was not associated with + /// any strong reference. /// - /// Returns null if the instance is removed. Otherwise, return the instanceId - /// of the removed instance. - int? removeInstance(Object instance) { - final int? instanceId = _instancesToInstanceIds[instance]; - if (instanceId != null) { - _instancesToInstanceIds.remove(instance); - _instanceIdsToInstances.remove(instanceId); + /// This does not remove the the weak referenced instance associtated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + return _strongInstances.remove(identifier) as T?; + } + + /// Retrieves the instance associated with identifier. + /// + /// The value returned is chosen from the following order: + /// + /// 1. A weakly referenced instance associated with identifier. + /// 2. If the only instance associated with identifier is a strongly + /// referenced instance, a copy of the instance is added as a weak reference + /// with the same identifier. Returning the newly created copy. + /// 3. If no instance is associated with identifier, returns null. + /// + /// This method also expects the host `InstanceManager` to have a strong + /// reference to the instance the identifier is associated with. + T? getInstanceWithWeakReference(int identifier) { + final Copyable? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final Copyable? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final Copyable copy = strongInstance.copy(); + _identifiers[copy] = identifier; + _weakInstances[identifier] = WeakReference(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; } - return instanceId; + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier(Copyable instance) { + return _identifiers[instance]; + } + + /// Adds a new instance that was instantiated by the host platform. + /// + /// In other words, the host platform wants to add a new instance that + /// represents an object on the host platform. Stored with [identifier]. + /// + /// Throws assertion error if the instance or its identifier has already been + /// added. + /// + /// Returns unique identifier of the [instance] added. + void addHostCreatedInstance(Copyable instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier(Copyable instance, int identifier) { + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final Copyable copy = instance.copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; } - /// Retrieve the Object paired with instanceId. - Object? getInstance(int instanceId) { - return _instanceIdsToInstances[instanceId]; + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); } - /// Retrieve the instanceId paired with instance. - int? getInstanceId(Object instance) { - return _instancesToInstanceIds[instance]; + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart index 1f0eb7bd7ded..47740247db73 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart @@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart'; -import 'src/instance_manager.dart'; import 'webview_android_widget.dart'; /// Builds an Android webview. @@ -55,8 +54,8 @@ class AndroidWebView implements WebViewPlatform { gestureRecognizers: gestureRecognizers, layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl, - creationParams: - InstanceManager.instance.getInstanceId(controller.webView), + creationParams: JavaObject.globalInstanceManager + .getIdentifier(controller.webView), creationParamsCodec: const StandardMessageCodec(), ), ); diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart index 61ec11012881..e89fb7d1512f 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart @@ -10,7 +10,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart'; -import 'src/instance_manager.dart'; import 'webview_android.dart'; import 'webview_android_widget.dart'; @@ -74,8 +73,8 @@ class SurfaceAndroidWebView extends AndroidWebView { // directionality. layoutDirection: Directionality.maybeOf(context) ?? TextDirection.ltr, - webViewIdentifier: - InstanceManager.instance.getInstanceId(controller.webView)!, + webViewIdentifier: JavaObject.globalInstanceManager + .getIdentifier(controller.webView)!, ) ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) ..addOnPlatformViewCreatedListener((int id) { diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index f76534196caf..a60c4f9cc313 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.9.2 +version: 2.9.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index cb2ea0fbf839..9d479e62161a 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -45,11 +45,11 @@ void main() { mockPlatformHostApi = MockTestWebViewHostApi(); TestWebViewHostApi.setup(mockPlatformHostApi); - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); webView = WebView(); - webViewInstanceId = instanceManager.getInstanceId(webView)!; + webViewInstanceId = instanceManager.getIdentifier(webView)!; }); test('create', () { @@ -206,11 +206,12 @@ void main() { ); final WebViewClient mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false); webView.setWebViewClient(mockWebViewClient); final int webViewClientInstanceId = - instanceManager.getInstanceId(mockWebViewClient)!; + instanceManager.getIdentifier(mockWebViewClient)!; verify(mockPlatformHostApi.setWebViewClient( webViewInstanceId, webViewClientInstanceId, @@ -224,12 +225,13 @@ void main() { ); final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); when(mockJavaScriptChannel.channelName).thenReturn('aChannel'); webView.addJavaScriptChannel(mockJavaScriptChannel); final int javaScriptChannelInstanceId = - instanceManager.getInstanceId(mockJavaScriptChannel)!; + instanceManager.getIdentifier(mockJavaScriptChannel)!; verify(mockPlatformHostApi.addJavaScriptChannel( webViewInstanceId, javaScriptChannelInstanceId, @@ -243,6 +245,7 @@ void main() { ); final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); when(mockJavaScriptChannel.channelName).thenReturn('aChannel'); expect( @@ -254,7 +257,7 @@ void main() { webView.removeJavaScriptChannel(mockJavaScriptChannel); final int javaScriptChannelInstanceId = - instanceManager.getInstanceId(mockJavaScriptChannel)!; + instanceManager.getIdentifier(mockJavaScriptChannel)!; verify(mockPlatformHostApi.removeJavaScriptChannel( webViewInstanceId, javaScriptChannelInstanceId, @@ -268,10 +271,11 @@ void main() { ); final DownloadListener mockDownloadListener = MockDownloadListener(); + when(mockDownloadListener.copy()).thenReturn(MockDownloadListener()); webView.setDownloadListener(mockDownloadListener); final int downloadListenerInstanceId = - instanceManager.getInstanceId(mockDownloadListener)!; + instanceManager.getIdentifier(mockDownloadListener)!; verify(mockPlatformHostApi.setDownloadListener( webViewInstanceId, downloadListenerInstanceId, @@ -285,6 +289,7 @@ void main() { instanceManager: instanceManager, ); final WebViewClient mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false); webView.setWebViewClient(mockWebViewClient); @@ -294,10 +299,11 @@ void main() { ); final WebChromeClient mockWebChromeClient = MockWebChromeClient(); + when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient()); webView.setWebChromeClient(mockWebChromeClient); final int webChromeClientInstanceId = - instanceManager.getInstanceId(mockWebChromeClient)!; + instanceManager.getIdentifier(mockWebChromeClient)!; verify(mockPlatformHostApi.setWebChromeClient( webViewInstanceId, webChromeClientInstanceId, @@ -312,7 +318,7 @@ void main() { WebSettings.api = WebSettingsHostApiImpl(instanceManager: instanceManager); final int webSettingsInstanceId = - instanceManager.getInstanceId(webView.settings)!; + instanceManager.getIdentifier(webView.settings)!; webView.release(); verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId)); @@ -333,7 +339,7 @@ void main() { late int webSettingsInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); TestWebViewHostApi.setup(MockTestWebViewHostApi()); WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); @@ -346,7 +352,7 @@ void main() { ); webSettings = WebSettings(WebView()); - webSettingsInstanceId = instanceManager.getInstanceId(webSettings)!; + webSettingsInstanceId = instanceManager.getIdentifier(webSettings)!; }); test('create', () { @@ -463,14 +469,16 @@ void main() { late int mockJavaScriptChannelInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = JavaScriptChannelFlutterApiImpl( instanceManager: instanceManager, ); mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); + mockJavaScriptChannelInstanceId = - instanceManager.tryAddInstance(mockJavaScriptChannel)!; + instanceManager.addDartCreatedInstance(mockJavaScriptChannel); }); test('postMessage', () { @@ -501,17 +509,20 @@ void main() { late int mockWebViewInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = WebViewClientFlutterApiImpl( instanceManager: instanceManager, ); mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); mockWebViewClientInstanceId = - instanceManager.tryAddInstance(mockWebViewClient)!; + instanceManager.addDartCreatedInstance(mockWebViewClient); mockWebView = MockWebView(); - mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!; + when(mockWebView.copy()).thenReturn(MockWebView()); + mockWebViewInstanceId = + instanceManager.addDartCreatedInstance(mockWebView); }); test('onPageStarted', () { @@ -621,14 +632,15 @@ void main() { late int mockDownloadListenerInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = DownloadListenerFlutterApiImpl( instanceManager: instanceManager, ); mockDownloadListener = MockDownloadListener(); + when(mockDownloadListener.copy()).thenReturn(MockDownloadListener()); mockDownloadListenerInstanceId = - instanceManager.tryAddInstance(mockDownloadListener)!; + instanceManager.addDartCreatedInstance(mockDownloadListener); }); test('onPageStarted', () { @@ -666,17 +678,21 @@ void main() { late int mockWebViewInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = WebChromeClientFlutterApiImpl( instanceManager: instanceManager, ); mockWebChromeClient = MockWebChromeClient(); + when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient()); + mockWebChromeClientInstanceId = - instanceManager.tryAddInstance(mockWebChromeClient)!; + instanceManager.addDartCreatedInstance(mockWebChromeClient); mockWebView = MockWebView(); - mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!; + when(mockWebView.copy()).thenReturn(MockWebView()); + mockWebViewInstanceId = + instanceManager.addDartCreatedInstance(mockWebView); }); test('onPageStarted', () { @@ -722,7 +738,7 @@ void main() { webStorage = WebStorage(); webStorageInstanceId = - WebStorage.api.instanceManager.getInstanceId(webStorage)!; + WebStorage.api.instanceManager.getIdentifier(webStorage)!; }); test('create', () { diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 72d1d414f915..db877e5870b6 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -59,7 +59,6 @@ class MockCookieManagerHostApi extends _i1.Mock /// A class which mocks [DownloadListener]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { MockDownloadListener() { _i1.throwOnMissingStub(this); @@ -81,7 +80,6 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { /// A class which mocks [JavaScriptChannel]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { MockJavaScriptChannel() { _i1.throwOnMissingStub(this); @@ -417,7 +415,6 @@ class MockTestAssetManagerHostApi extends _i1.Mock /// A class which mocks [WebChromeClient]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { MockWebChromeClient() { _i1.throwOnMissingStub(this); @@ -436,7 +433,6 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); @@ -590,7 +586,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { /// A class which mocks [WebViewClient]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { MockWebViewClient() { _i1.throwOnMissingStub(this); diff --git a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart index 3aeb005efb86..dd3dcca29fe8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart @@ -7,29 +7,137 @@ import 'package:webview_flutter_android/src/instance_manager.dart'; void main() { group('InstanceManager', () { - late InstanceManager testInstanceManager; + test('addHostCreatedInstance', () { + final CopyableObject object = CopyableObject(); - setUp(() { - testInstanceManager = InstanceManager(); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect( + () => instanceManager.addHostCreatedInstance(object, 0), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance(CopyableObject(), 0), + throwsAssertionError, + ); + }); + + test('addFlutterCreatedInstance', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addDartCreatedInstance(object); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + final CopyableObject object = CopyableObject(); + + int? weakInstanceId; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); + }); + + test('removeWeakReference removes only weak reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + final CopyableObject copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); + }); + + test('removeStrongReference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); }); - test('tryAddInstance', () { - final Object object = Object(); + test('removeStrongReference removes only strong reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); - expect(testInstanceManager.tryAddInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), 0); - expect(testInstanceManager.getInstance(0), object); - expect(testInstanceManager.tryAddInstance(object), null); + instanceManager.addHostCreatedInstance(object, 0); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); }); - test('removeInstance', () { - final Object object = Object(); - testInstanceManager.tryAddInstance(object); + test('getInstance can add a new weak reference', () { + final CopyableObject object = CopyableObject(); - expect(testInstanceManager.removeInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), null); - expect(testInstanceManager.getInstance(0), null); - expect(testInstanceManager.removeInstance(object), null); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + + final CopyableObject newWeakCopy = + instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); }); }); } + +class CopyableObject with Copyable { + @override + Copyable copy() { + return CopyableObject(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 2432b35a4814..2fa75af72d69 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -828,7 +828,7 @@ void main() { // of WebView itstelf. mockPlatformHostApi = MockTestWebViewHostApi(); TestWebViewHostApi.setup(mockPlatformHostApi); - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); android_webview.WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); }); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index f9e8838500d5..04c2d778ed19 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -64,7 +64,6 @@ class MockFlutterAssetManager extends _i1.Mock /// A class which mocks [WebSettings]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebSettings extends _i1.Mock implements _i2.WebSettings { MockWebSettings() { _i1.throwOnMissingStub(this); @@ -140,7 +139,6 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { /// A class which mocks [WebStorage]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebStorage extends _i1.Mock implements _i2.WebStorage { MockWebStorage() { _i1.throwOnMissingStub(this); @@ -159,7 +157,6 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); diff --git a/script/tool/README.md b/script/tool/README.md index d3beb53a1103..225cda2ef265 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -105,6 +105,8 @@ cd dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android --no-integration --packages plugin_name # Run all tests for macOS: dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages plugin_name +# Run all tests for Windows: +dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --windows --packages plugin_name ``` ### Update README.md from Example Sources diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index 437bbf6df370..a03d643bdab0 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -35,7 +35,7 @@ class PackageChangeState { /// [changedPaths] should be a list of POSIX-style paths from a common root, /// and [relativePackagePath] should be the path to [package] from that same /// root. Commonly these will come from `gitVersionFinder.getChangedFiles()` -/// and `getRelativePoixPath(package.directory, gitDir.path)` respectively; +/// and `getRelativePosixPath(package.directory, gitDir.path)` respectively; /// they are arguments mainly to allow for caching the changed paths for an /// entire command run. PackageChangeState checkPackageChangeState( From 9b4bf453090bdfe079b577b09e3c1e34791ea373 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 3 Aug 2022 09:51:45 -0700 Subject: [PATCH 02/28] Bump down dart sdk version --- packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 4a8353fbf2dc..f254d3564b6d 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: '>=2.19.0-55.0.dev <3.0.0' + sdk: '>=2.14.0 <3.0.0' flutter: ">=2.5.0" dependencies: From bcd03ffee2532f0174b20fbf79ccd58aedf976ff Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 3 Aug 2022 15:26:55 -0700 Subject: [PATCH 03/28] Rev 1 --- .../camera_android_camerax/CameraAndroidCameraxPlugin.java | 4 ++-- .../camera_android_camerax_example/MainActivity.java | 3 +-- .../camera/camera_android_camerax/example/lib/main.dart | 3 ++- .../camera/camera_android_camerax/example/pubspec.yaml | 2 +- .../camera_android_camerax/example/test/widget_test.dart | 4 ++-- .../camera_android_camerax/lib/camera_android_camerax.dart | 1 - .../lib/camera_android_camerax_method_channel.dart | 3 ++- .../lib/camera_android_camerax_platform_interface.dart | 3 ++- .../test/camera_android_camerax_method_channel_test.dart | 3 ++- .../test/camera_android_camerax_test.dart | 7 ++++--- 10 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java index 4663357a5865..1a0f154c2ab4 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java @@ -1,7 +1,6 @@ package io.flutter.plugins.camera_android_camerax; import androidx.annotation.NonNull; - import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -18,7 +17,8 @@ public class CameraAndroidCameraxPlugin implements FlutterPlugin, MethodCallHand @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); + channel = + new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); channel.setMethodCallHandler(this); } diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java index 1886d5c8c2b6..955cf2116b13 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java @@ -2,5 +2,4 @@ import io.flutter.embedding.android.FlutterActivity; -public class MainActivity extends FlutterActivity { -} +public class MainActivity extends FlutterActivity {} diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index 70475c8831b2..1045bb8acf9d 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -32,7 +32,8 @@ class _MyAppState extends State { // We also handle the message potentially returning null. try { platformVersion = - await _cameraAndroidCameraxPlugin.getPlatformVersion() ?? 'Unknown platform version'; + await _cameraAndroidCameraxPlugin.getPlatformVersion() ?? + 'Unknown platform version'; } on PlatformException { platformVersion = 'Failed to get platform version.'; } diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 19fffbeaf721..65699528cebd 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -6,7 +6,7 @@ description: Demonstrates how to use the camera_android_camerax plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: '>=2.19.0-55.0.dev <3.0.0' + sdk: '>=2.14.0 <3.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/packages/camera/camera_android_camerax/example/test/widget_test.dart b/packages/camera/camera_android_camerax/example/test/widget_test.dart index a657ebef4722..67eb5ae210b3 100644 --- a/packages/camera/camera_android_camerax/example/test/widget_test.dart +++ b/packages/camera/camera_android_camerax/example/test/widget_test.dart @@ -18,8 +18,8 @@ void main() { // Verify that platform version is retrieved. expect( find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart index b4a1b762cd7f..95c83899666b 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -1,4 +1,3 @@ - import 'camera_android_camerax_platform_interface.dart'; class CameraAndroidCamerax { diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart index d5a2dae1b92a..a0b84beda2b5 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart @@ -11,7 +11,8 @@ class MethodChannelCameraAndroidCamerax extends CameraAndroidCameraxPlatform { @override Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); + final version = + await methodChannel.invokeMethod('getPlatformVersion'); return version; } } diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart index 09980acfbf94..d39effa38f36 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart @@ -8,7 +8,8 @@ abstract class CameraAndroidCameraxPlatform extends PlatformInterface { static final Object _token = Object(); - static CameraAndroidCameraxPlatform _instance = MethodChannelCameraAndroidCamerax(); + static CameraAndroidCameraxPlatform _instance = + MethodChannelCameraAndroidCamerax(); /// The default instance of [CameraAndroidCameraxPlatform] to use. /// diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart index 8a0176129611..7a6905c13885 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart @@ -3,7 +3,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; void main() { - MethodChannelCameraAndroidCamerax platform = MethodChannelCameraAndroidCamerax(); + MethodChannelCameraAndroidCamerax platform = + MethodChannelCameraAndroidCamerax(); const MethodChannel channel = MethodChannel('camera_android_camerax'); TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart index 0c215729942e..514195eb0eec 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart @@ -7,13 +7,13 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockCameraAndroidCameraxPlatform with MockPlatformInterfaceMixin implements CameraAndroidCameraxPlatform { - @override Future getPlatformVersion() => Future.value('42'); } void main() { - final CameraAndroidCameraxPlatform initialPlatform = CameraAndroidCameraxPlatform.instance; + final CameraAndroidCameraxPlatform initialPlatform = + CameraAndroidCameraxPlatform.instance; test('$MethodChannelCameraAndroidCamerax is the default instance', () { expect(initialPlatform, isInstanceOf()); @@ -21,7 +21,8 @@ void main() { test('getPlatformVersion', () async { CameraAndroidCamerax cameraAndroidCameraxPlugin = CameraAndroidCamerax(); - MockCameraAndroidCameraxPlatform fakePlatform = MockCameraAndroidCameraxPlatform(); + MockCameraAndroidCameraxPlatform fakePlatform = + MockCameraAndroidCameraxPlatform(); CameraAndroidCameraxPlatform.instance = fakePlatform; expect(await cameraAndroidCameraxPlugin.getPlatformVersion(), '42'); From d66030ebee2b7dbb4030df66675170acae15cbb3 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 3 Aug 2022 15:53:53 -0700 Subject: [PATCH 04/28] Rev 2 --- .../camera/camera_android_camerax/AUTHORS | 66 +++++++++++++++++++ .../camera/camera_android_camerax/README.md | 14 +--- .../android/build.gradle | 2 +- .../CameraAndroidCameraxPlugin.java | 4 ++ .../CameraxPluginTest.java | 3 + .../camera_android_camerax/example/README.md | 13 ---- .../example/analysis_options.yaml | 29 -------- .../example/android/app/build.gradle | 2 +- .../MainActivity.java | 4 ++ .../example/lib/main.dart | 6 +- .../example/test/widget_test.dart | 4 ++ .../lib/camera_android_camerax.dart | 4 ++ ...camera_android_camerax_method_channel.dart | 4 ++ ...ra_android_camerax_platform_interface.dart | 4 ++ .../camera_android_camerax/pubspec.yaml | 23 ++++--- ...a_android_camerax_method_channel_test.dart | 4 ++ .../test/camera_android_camerax_test.dart | 4 ++ 17 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 packages/camera/camera_android_camerax/AUTHORS create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java delete mode 100644 packages/camera/camera_android_camerax/example/analysis_options.yaml diff --git a/packages/camera/camera_android_camerax/AUTHORS b/packages/camera/camera_android_camerax/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/camera/camera_android_camerax/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index 678d0abff73f..06d837ac7214 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -1,15 +1,3 @@ # camera_android_camerax -A new Flutter plugin project. - -## Getting Started - -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. - -For help getting started with Flutter development, view the -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. - +An implementation of the camera plugin on Android using CameraX. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index cf48a4bfe804..98753a65396d 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -1,4 +1,4 @@ -group 'io.flutter.plugins.camera_android_camerax' +group 'io.flutter.plugins.camera' version '1.0' buildscript { diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java index 1a0f154c2ab4..a7bef36b9e61 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java @@ -1,3 +1,7 @@ +// 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.camera_android_camerax; import androidx.annotation.NonNull; diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java new file mode 100644 index 000000000000..489917129706 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java @@ -0,0 +1,3 @@ +public class CameraxPluginTest { + +} diff --git a/packages/camera/camera_android_camerax/example/README.md b/packages/camera/camera_android_camerax/example/README.md index e3817a807e77..438e446d1921 100644 --- a/packages/camera/camera_android_camerax/example/README.md +++ b/packages/camera/camera_android_camerax/example/README.md @@ -1,16 +1,3 @@ # camera_android_camerax_example Demonstrates how to use the camera_android_camerax plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/camera/camera_android_camerax/example/analysis_options.yaml b/packages/camera/camera_android_camerax/example/analysis_options.yaml deleted file mode 100644 index 61b6c4de17c9..000000000000 --- a/packages/camera/camera_android_camerax/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle index 8a96215d32af..795067383175 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -36,7 +36,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "io.flutter.plugins.camera_android_camerax_example" + applicationId "io.flutter.plugins.cameraexample" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java index 955cf2116b13..903a889e8be2 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/camera_android_camerax_example/MainActivity.java @@ -1,3 +1,7 @@ +// 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.camera_android_camerax_example; import io.flutter.embedding.android.FlutterActivity; diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index 1045bb8acf9d..bcbdd4dd7985 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -1,3 +1,7 @@ +// 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. + import 'package:flutter/material.dart'; import 'dart:async'; @@ -9,7 +13,7 @@ void main() { } class MyApp extends StatefulWidget { - const MyApp({super.key}); + const MyApp({Key? key}) : super(key: key); @override State createState() => _MyAppState(); diff --git a/packages/camera/camera_android_camerax/example/test/widget_test.dart b/packages/camera/camera_android_camerax/example/test/widget_test.dart index 67eb5ae210b3..204d38f4e752 100644 --- a/packages/camera/camera_android_camerax/example/test/widget_test.dart +++ b/packages/camera/camera_android_camerax/example/test/widget_test.dart @@ -1,3 +1,7 @@ +// 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. + // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart index 95c83899666b..dfc3d75f6a8f 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -1,3 +1,7 @@ +// 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. + import 'camera_android_camerax_platform_interface.dart'; class CameraAndroidCamerax { diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart index a0b84beda2b5..a1e83fa88b37 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart @@ -1,3 +1,7 @@ +// 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. + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart index d39effa38f36..b697398342a8 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart @@ -1,3 +1,7 @@ +// 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. + import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'camera_android_camerax_method_channel.dart'; diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index f254d3564b6d..049e0848b9e2 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -1,5 +1,7 @@ name: camera_android_camerax description: A new Flutter plugin project. +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android_camerax +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.0.1 homepage: @@ -7,16 +9,6 @@ environment: sdk: '>=2.14.0 <3.0.0' flutter: ">=2.5.0" -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^2.0.0 - # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -33,6 +25,7 @@ flutter: # All these are used by the tooling to maintain consistency when # adding or updating assets for this project. plugin: + implements: camera platforms: android: package: io.flutter.plugins.camera_android_camerax @@ -68,3 +61,13 @@ flutter: # # For details regarding fonts in packages, see # https://flutter.dev/custom-fonts/#from-packages + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart index 7a6905c13885..c6959e30eb5b 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart @@ -1,3 +1,7 @@ +// 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. + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart index 514195eb0eec..be3665c53102 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart @@ -1,3 +1,7 @@ +// 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. + import 'package:flutter_test/flutter_test.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; From cda82819dd0a5f804a4efc7369922b7981ab54cd Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 09:42:05 -0700 Subject: [PATCH 05/28] Formatting --- packages/camera/camera_android_camerax/analysis_options.yaml | 4 ---- .../plugins/camera_android_camerax/CameraxPluginTest.java | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 packages/camera/camera_android_camerax/analysis_options.yaml diff --git a/packages/camera/camera_android_camerax/analysis_options.yaml b/packages/camera/camera_android_camerax/analysis_options.yaml deleted file mode 100644 index a5744c1cfbe7..000000000000 --- a/packages/camera/camera_android_camerax/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java index 489917129706..bc673fd9546a 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java @@ -1,3 +1 @@ -public class CameraxPluginTest { - -} +public class CameraxPluginTest {} From d505dbbb0a353e23ae2c4c5f37266d6baeaa3517 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 10:06:18 -0700 Subject: [PATCH 06/28] Rev 3 --- .../example/android/app/build.gradle | 1 + .../io/flutter/plugins/DartIntegrationTest.java | 14 ++++++++++++++ .../MainActivityTest.java | 17 +++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle index 795067383175..569d522e60bf 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -43,6 +43,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java new file mode 100644 index 000000000000..3f9543677128 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java @@ -0,0 +1,17 @@ +// 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.camera_android_camerax; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} From 35d4fd0231858acb667db37b732d6c6ca8f810f7 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 10:40:22 -0700 Subject: [PATCH 07/28] Rev 4 --- .../CameraxPluginTest.java | 4 ++ .../example/android/app/build.gradle | 5 +++ .../MainActivityTest.java | 19 +++++++-- .../example/lib/main.dart | 42 ++----------------- .../example/pubspec.yaml | 12 +++--- .../example/test/widget_test.dart | 3 +- .../lib/camera_android_camerax.dart | 2 + ...camera_android_camerax_method_channel.dart | 5 ++- ...ra_android_camerax_platform_interface.dart | 2 + .../camera_android_camerax/pubspec.yaml | 2 +- ...a_android_camerax_method_channel_test.dart | 4 +- .../test/camera_android_camerax_test.dart | 11 ++--- 12 files changed, 50 insertions(+), 61 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java index bc673fd9546a..358a8a7cd454 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java @@ -1 +1,5 @@ +// 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. + public class CameraxPluginTest {} diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle index 569d522e60bf..255e7de35520 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -58,3 +58,8 @@ android { flutter { source '../..' } + +dependencies { + testImplementation 'junit:junit:4.13.2' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java index 3f9543677128..c629330241f4 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java @@ -2,16 +2,27 @@ // 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_android_camerax; +package io.flutter.plugins.camera_android_camerax_example; -import androidx.test.rule.ActivityTestRule; +import static org.junit.Assert.assertTrue; + +import androidx.test.core.app.ActivityScenario; import dev.flutter.plugins.integration_test.FlutterTestRunner; import io.flutter.plugins.DartIntegrationTest; -import org.junit.Rule; +import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; @DartIntegrationTest @RunWith(FlutterTestRunner.class) public class MainActivityTest { - @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); + @Before + public void setUp() throws Exception { + ActivityScenario.launch(MainActivity.class); + } + + @Test + public void fakeTest() { + assertTrue(true); + } } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index bcbdd4dd7985..c5391096374e 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -3,16 +3,14 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:camera_android_camerax/camera_android_camerax.dart'; void main() { runApp(const MyApp()); } +/// Example app class MyApp extends StatefulWidget { + /// App instantiation const MyApp({Key? key}) : super(key: key); @override @@ -20,38 +18,6 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - String _platformVersion = 'Unknown'; - final _cameraAndroidCameraxPlugin = CameraAndroidCamerax(); - - @override - void initState() { - super.initState(); - initPlatformState(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - try { - platformVersion = - await _cameraAndroidCameraxPlugin.getPlatformVersion() ?? - 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); - } - @override Widget build(BuildContext context) { return MaterialApp( @@ -59,8 +25,8 @@ class _MyAppState extends State { appBar: AppBar( title: const Text('Plugin example app'), ), - body: Center( - child: Text('Running on: $_platformVersion\n'), + body: const Center( + child: Text('Hello, world!'), ), ), ); diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 65699528cebd..5c4f0a29d7a1 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -15,9 +15,6 @@ environment: # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: - flutter: - sdk: flutter - camera_android_camerax: # When depending on this package from a real application you should use: # camera_android_camerax: ^x.y.z @@ -25,15 +22,13 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - -dev_dependencies: - flutter_test: + flutter: sdk: flutter +dev_dependencies: # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your @@ -41,6 +36,9 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/packages/camera/camera_android_camerax/example/test/widget_test.dart b/packages/camera/camera_android_camerax/example/test/widget_test.dart index 204d38f4e752..82681214b185 100644 --- a/packages/camera/camera_android_camerax/example/test/widget_test.dart +++ b/packages/camera/camera_android_camerax/example/test/widget_test.dart @@ -9,11 +9,10 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. +import 'package:camera_android_camerax_example/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:camera_android_camerax_example/main.dart'; - void main() { testWidgets('Verify Platform version', (WidgetTester tester) async { // Build our app and trigger a frame. diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart index dfc3d75f6a8f..40f8c703fa9f 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -4,7 +4,9 @@ import 'camera_android_camerax_platform_interface.dart'; +/// Android Camera implented with the CameraX library. class CameraAndroidCamerax { + /// Returns platform version of this instance. Future getPlatformVersion() { return CameraAndroidCameraxPlatform.instance.getPlatformVersion(); } diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart index a1e83fa88b37..699a3667d14d 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart @@ -11,11 +11,12 @@ import 'camera_android_camerax_platform_interface.dart'; class MethodChannelCameraAndroidCamerax extends CameraAndroidCameraxPlatform { /// The method channel used to interact with the native platform. @visibleForTesting - final methodChannel = const MethodChannel('camera_android_camerax'); + final MethodChannel methodChannel = + const MethodChannel('camera_android_camerax'); @override Future getPlatformVersion() async { - final version = + final String? version = await methodChannel.invokeMethod('getPlatformVersion'); return version; } diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart index b697398342a8..03554caf3245 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart @@ -6,6 +6,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'camera_android_camerax_method_channel.dart'; +/// The platform interface for the Android Camera implemented with CameraX. abstract class CameraAndroidCameraxPlatform extends PlatformInterface { /// Constructs a CameraAndroidCameraxPlatform. CameraAndroidCameraxPlatform() : super(token: _token); @@ -28,6 +29,7 @@ abstract class CameraAndroidCameraxPlatform extends PlatformInterface { _instance = instance; } + /// Returns the platform version of this isntance. Future getPlatformVersion() { throw UnimplementedError('platformVersion() has not been implemented.'); } diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 049e0848b9e2..d0bd12dedd53 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -68,6 +68,6 @@ dependencies: plugin_platform_interface: ^2.0.2 dev_dependencies: + flutter_lints: ^2.0.0 flutter_test: sdk: flutter - flutter_lints: ^2.0.0 diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart index c6959e30eb5b..0a1ea847e1a5 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; void main() { - MethodChannelCameraAndroidCamerax platform = + final MethodChannelCameraAndroidCamerax platform = MethodChannelCameraAndroidCamerax(); const MethodChannel channel = MethodChannel('camera_android_camerax'); diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart index be3665c53102..05ff46c1c55d 100644 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter_test/flutter_test.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; -import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; +import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockCameraAndroidCameraxPlatform with MockPlatformInterfaceMixin implements CameraAndroidCameraxPlatform { @override - Future getPlatformVersion() => Future.value('42'); + Future getPlatformVersion() => Future.value('42'); } void main() { @@ -24,8 +24,9 @@ void main() { }); test('getPlatformVersion', () async { - CameraAndroidCamerax cameraAndroidCameraxPlugin = CameraAndroidCamerax(); - MockCameraAndroidCameraxPlatform fakePlatform = + final CameraAndroidCamerax cameraAndroidCameraxPlugin = + CameraAndroidCamerax(); + final MockCameraAndroidCameraxPlatform fakePlatform = MockCameraAndroidCameraxPlatform(); CameraAndroidCameraxPlatform.instance = fakePlatform; From b399d6cb09f012bf73837d5ead656ba3bc7890aa Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 10:51:54 -0700 Subject: [PATCH 08/28] Rev 5 --- .../camera/camera_android_camerax/LICENSE | 26 ++++++++++++++++++- .../android/app/src/main/AndroidManifest.xml | 1 - .../example/test/widget_test.dart | 16 ++---------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera_android_camerax/LICENSE b/packages/camera/camera_android_camerax/LICENSE index ba75c69f7f21..c6823b81eb84 100644 --- a/packages/camera/camera_android_camerax/LICENSE +++ b/packages/camera/camera_android_camerax/LICENSE @@ -1 +1,25 @@ -TODO: Add your license here. +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml index 363f1d69cb5b..e47d46b19cb1 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ android:icon="@mipmap/ic_launcher"> - widget is Text && widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); + testWidgets('Fake test', (WidgetTester tester) async { + expect(true, isTrue); }); } From a963c56339efbd8aded212b360f2d58d7dd9e2fb Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 11:05:14 -0700 Subject: [PATCH 09/28] Rev 6 --- .../camera_android/android/build.gradle | 2 +- .../camera_android_camerax/android/.gitignore | 9 ---- .../camera_android_camerax/example/.gitignore | 44 ------------------- 3 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 packages/camera/camera_android_camerax/android/.gitignore delete mode 100644 packages/camera/camera_android_camerax/example/.gitignore diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 1967ebaf0236..0e989fd1ac47 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -30,7 +30,7 @@ android { compileSdkVersion 31 defaultConfig { - targetSdkVersion 31 + targetSdkVersion 30 minSdkVersion 21 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/packages/camera/camera_android_camerax/android/.gitignore b/packages/camera/camera_android_camerax/android/.gitignore deleted file mode 100644 index 161bdcdaf88c..000000000000 --- a/packages/camera/camera_android_camerax/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.cxx diff --git a/packages/camera/camera_android_camerax/example/.gitignore b/packages/camera/camera_android_camerax/example/.gitignore deleted file mode 100644 index 24476c5d1eb5..000000000000 --- a/packages/camera/camera_android_camerax/example/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release From 8eec39c3e65811bca64442f025506f004fa9494b Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 11:44:29 -0700 Subject: [PATCH 10/28] Rev 7 --- packages/camera/camera_android/android/build.gradle | 2 +- .../android/src/main/AndroidManifest.xml | 2 +- .../CameraAndroidCameraxPlugin.java | 2 +- .../CameraxPluginTest.java | 0 .../example/android/.gitignore | 13 ------------- .../example/android/app/build.gradle | 2 +- .../MainActivityTest.java | 2 +- .../android/app/src/main/AndroidManifest.xml | 2 +- 8 files changed, 6 insertions(+), 19 deletions(-) rename packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/{camera_android_camerax => camera}/CameraAndroidCameraxPlugin.java (96%) rename packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/{camera_android_camerax => camera}/CameraxPluginTest.java (100%) delete mode 100644 packages/camera/camera_android_camerax/example/android/.gitignore rename packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/{camera_android_camerax_example => cameraexample}/MainActivityTest.java (92%) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 0e989fd1ac47..1967ebaf0236 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -30,7 +30,7 @@ android { compileSdkVersion 31 defaultConfig { - targetSdkVersion 30 + targetSdkVersion 31 minSdkVersion 21 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml index 531d101a2619..e1e6896bfcc1 100644 --- a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml +++ b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="io.flutter.plugins.camera"> diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera/CameraAndroidCameraxPlugin.java similarity index 96% rename from packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java rename to packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera/CameraAndroidCameraxPlugin.java index a7bef36b9e61..62cfceba4470 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera_android_camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camera/CameraAndroidCameraxPlugin.java @@ -2,7 +2,7 @@ // 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_android_camerax; +package io.flutter.plugins.camera; import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera/CameraxPluginTest.java similarity index 100% rename from packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera_android_camerax/CameraxPluginTest.java rename to packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camera/CameraxPluginTest.java diff --git a/packages/camera/camera_android_camerax/example/android/.gitignore b/packages/camera/camera_android_camerax/example/android/.gitignore deleted file mode 100644 index 6f568019d3c6..000000000000 --- a/packages/camera/camera_android_camerax/example/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle index 255e7de35520..d1cb6d61bb98 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -40,7 +40,7 @@ android { // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/MainActivityTest.java similarity index 92% rename from packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java rename to packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/MainActivityTest.java index c629330241f4..81b0f19d7a3a 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/camera_android_camerax_example/MainActivityTest.java +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/MainActivityTest.java @@ -2,7 +2,7 @@ // 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_android_camerax_example; +package io.flutter.plugins.cameraexample; import static org.junit.Assert.assertTrue; diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml index e47d46b19cb1..c14cafcc64a1 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="io.flutter.plugins.cameraexample"> Date: Thu, 4 Aug 2022 11:45:34 -0700 Subject: [PATCH 11/28] Add changelog message --- packages/camera/camera_android_camerax/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 41cc7d8192ec..b50110d0d10f 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,3 @@ ## 0.0.1 -* TODO: Describe initial release. +* Create camera_android_camerax plugin for development. From 921fb0d723a2c8a397aa042e558e64a029507b97 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 4 Aug 2022 11:51:49 -0700 Subject: [PATCH 12/28] Rev 8 --- .../example/android/app/src/debug/AndroidManifest.xml | 2 +- .../example/android/app/src/profile/AndroidManifest.xml | 2 +- packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml index 51f3479db75b..2ba93e63596b 100644 --- a/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml +++ b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package=""io.flutter.plugins.cameraexample"">