From 15fec3d41de61fe1dc08a4970ecac3eb04730c52 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Sep 2022 13:02:26 -0400 Subject: [PATCH 01/49] Roll Flutter from 66c306b45228 to 4cd734ae3a1c (24 revisions) (#6379) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3e933cd88a72..af8df44327ff 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -66c306b452282016e4a146a8f1c11f44836560f1 +4cd734ae3a1cc0c8f132ba07e6d2e0a3253c53e7 From d4d3bd76fd54b8d3a663115a3704c7970082f9ee Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 9 Sep 2022 13:52:16 -0400 Subject: [PATCH 02/49] [tools] Improves version-check logic (#6354) Improves the logic used to determine whether to require a version and/or CHANGELOG change: - Removes the requirement that dev-only (e.g., test) changes update the CHANGELOG, since in practice we were essentially always overriding in that case. - Adds file-level analysis of `build.gradle` files to determine whether they are only changing test dependencies. - Improves the "is this a published example file" logic to better match pub.dev's logic, to fix some false positives and false negatives (e.g., `rfw`'s `example//lib/main.dart` being considered published). Removes the no-longer-necessary special-case handling of some Dependabot PRs, as well as the PR-description-based system it was built on (and that turned out not to be very useful due to the way `CIRRUS_CHANGE_MESSAGE` actually worked). `build.gradle` analysis should not cover all such cases, and without the need to hard-code them by package name. --- .cirrus.yml | 12 +- script/tool/CHANGELOG.md | 10 + .../lib/src/common/git_version_finder.dart | 21 ++ .../lib/src/common/package_state_utils.dart | 153 +++++++-- .../lib/src/update_release_info_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 94 +---- script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 214 +++++++++++- .../tool/test/version_check_command_test.dart | 323 ++---------------- 9 files changed, 407 insertions(+), 424 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 21c7c0d93581..7a8e31dae0fb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -85,20 +85,16 @@ task: - cd script/tool - dart pub run test - name: publishable - env: - CHANGE_DESC: "$TMPDIR/change-description.txt" version_check_script: - # For pre-submit, pass the PR label, as well as the PR description or - # incremental commit message (for Dependabot checks), to the script to - # allow for version check overrides. + # For pre-submit, pass the PR labels to the script to allow for version + # check overrides. # For post-submit, ignore platform version breaking version changes and - # missing version/CHANGELOG detection since the overrides aren't + # missing version/CHANGELOG detection since the labels aren't # available outside of the context of the PR. - if [[ $CIRRUS_PR == "" ]]; then - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks - else - - echo "$CIRRUS_CHANGE_MESSAGE" > "$CHANGE_DESC" - - ./script/tool_runner.sh version-check --check-for-missing-changes --change-description-file="$CHANGE_DESC" --pr-labels="$CIRRUS_PR_LABELS" + - ./script/tool_runner.sh version-check --check-for-missing-changes --pr-labels="$CIRRUS_PR_LABELS" - fi publish_check_script: ./script/tool_runner.sh publish-check - name: format diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index ebfbe80b0184..8dbb4840a672 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.10.0 + +* Improves the logic in `version-check` to determine what changes don't require + version changes, as well as making any dev-only changes also not require + changelog changes since in practice we almost always override the check in + that case. +* Removes special-case handling of Dependabot PRs, and the (fragile) + `--change-description-file` flag was only still used for that case, as + the improved diff analysis now handles that case more robustly. + ## 0.9.3 * Raises minimum `compileSdkVersion` to 32 for the `all-plugins-app` command. diff --git a/script/tool/lib/src/common/git_version_finder.dart b/script/tool/lib/src/common/git_version_finder.dart index 32d30e60feb5..eb8fba6b76b8 100644 --- a/script/tool/lib/src/common/git_version_finder.dart +++ b/script/tool/lib/src/common/git_version_finder.dart @@ -50,6 +50,27 @@ class GitVersionFinder { return changedFiles.toList(); } + /// Get a list of all the changed files. + Future> getDiffContents({ + String? targetPath, + bool includeUncommitted = false, + }) async { + final String baseSha = await getBaseSha(); + final io.ProcessResult diffCommand = await baseGitDir.runCommand([ + 'diff', + baseSha, + if (!includeUncommitted) 'HEAD', + if (targetPath != null) ...['--', targetPath], + ]); + final String diffStdout = diffCommand.stdout.toString(); + if (diffStdout.isEmpty) { + return []; + } + final List changedFiles = diffStdout.split('\n') + ..removeWhere((String element) => element.isEmpty); + return changedFiles.toList(); + } + /// Get the package version specified in the pubspec file in `pubspecPath` and /// at the revision of `gitRef` (defaulting to the base if not provided). Future getPackageVersion(String pubspecPath, diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index a03d643bdab0..1cff65bb6b0c 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; @@ -14,6 +16,7 @@ class PackageChangeState { const PackageChangeState({ required this.hasChanges, required this.hasChangelogChange, + required this.needsChangelogChange, required this.needsVersionChange, }); @@ -26,6 +29,10 @@ class PackageChangeState { /// True if any changes in the package require a version change according /// to repository policy. final bool needsVersionChange; + + /// True if any changes in the package require a CHANGELOG change according + /// to repository policy. + final bool needsChangelogChange; } /// Checks [package] against [changedPaths] to determine what changes it has @@ -38,11 +45,15 @@ class PackageChangeState { /// 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( +/// +/// If [git] is provided, [changedPaths] must be repository-relative +/// paths, and change type detection can use file diffs in addition to paths. +Future checkPackageChangeState( RepositoryPackage package, { required List changedPaths, required String relativePackagePath, -}) { + GitVersionFinder? git, +}) async { final String packagePrefix = relativePackagePath.endsWith('/') ? relativePackagePath : '$relativePackagePath/'; @@ -50,6 +61,7 @@ PackageChangeState checkPackageChangeState( bool hasChanges = false; bool hasChangelogChange = false; bool needsVersionChange = false; + bool needsChangelogChange = false; for (final String path in changedPaths) { // Only consider files within the package. if (!path.startsWith(packagePrefix)) { @@ -62,34 +74,131 @@ PackageChangeState checkPackageChangeState( if (components.isEmpty) { continue; } - final bool isChangelog = components.first == 'CHANGELOG.md'; - if (isChangelog) { + + if (components.first == 'CHANGELOG.md') { hasChangelogChange = true; + continue; } - if (!needsVersionChange && - !isChangelog && - // One of a few special files example will be shown on pub.dev, but for - // anything else in the example publishing has no purpose. - !(components.first == 'example' && - !{'main.dart', 'readme.md', 'example.md'} - .contains(components.last.toLowerCase())) && - // Changes to tests don't need to be published. - !components.contains('test') && - !components.contains('androidTest') && - !components.contains('RunnerTests') && - !components.contains('RunnerUITests') && - // The top-level "tool" directory is for non-client-facing utility code, - // so doesn't need to be published. - components.first != 'tool' && - // Ignoring lints doesn't affect clients. - !components.contains('lint-baseline.xml')) { - needsVersionChange = true; + if (!needsVersionChange) { + // Developer-only changes don't need version changes or changelog changes. + if (await _isDevChange(components, git: git, repoPath: path)) { + continue; + } + + // Some other changes don't need version changes, but might benefit from + // changelog changes. + needsChangelogChange = true; + if ( + // One of a few special files example will be shown on pub.dev, but + // for anything else in the example publishing has no purpose. + !_isUnpublishedExampleChange(components, package)) { + needsVersionChange = true; + } } } return PackageChangeState( hasChanges: hasChanges, hasChangelogChange: hasChangelogChange, + needsChangelogChange: needsChangelogChange, needsVersionChange: needsVersionChange); } + +bool _isTestChange(List pathComponents) { + return pathComponents.contains('test') || + pathComponents.contains('androidTest') || + pathComponents.contains('RunnerTests') || + pathComponents.contains('RunnerUITests'); +} + +// True if the given file is an example file other than the one that will be +// published according to https://dart.dev/tools/pub/package-layout#examples. +// +// This is not exhastive; it currently only handles variations we actually have +// in our repositories. +bool _isUnpublishedExampleChange( + List pathComponents, RepositoryPackage package) { + if (pathComponents.first != 'example') { + return false; + } + final List exampleComponents = pathComponents.sublist(1); + if (exampleComponents.isEmpty) { + return false; + } + + final Directory exampleDirectory = + package.directory.childDirectory('example'); + + // Check for example.md/EXAMPLE.md first, as that has priority. If it's + // present, any other example file is unpublished. + final bool hasExampleMd = + exampleDirectory.childFile('example.md').existsSync() || + exampleDirectory.childFile('EXAMPLE.md').existsSync(); + if (hasExampleMd) { + return !(exampleComponents.length == 1 && + exampleComponents.first.toLowerCase() == 'example.md'); + } + + // Most packages have an example/lib/main.dart (or occasionally + // example/main.dart), so check for that. The other naming variations aren't + // currently used. + const String mainName = 'main.dart'; + final bool hasExampleCode = + exampleDirectory.childDirectory('lib').childFile(mainName).existsSync() || + exampleDirectory.childFile(mainName).existsSync(); + if (hasExampleCode) { + // If there is an example main, only that example file is published. + return !((exampleComponents.length == 1 && + exampleComponents.first == mainName) || + (exampleComponents.length == 2 && + exampleComponents.first == 'lib' && + exampleComponents[1] == mainName)); + } + + // If there's no example code either, the example README.md, if any, is the + // file that will be published. + return exampleComponents.first.toLowerCase() != 'readme.md'; +} + +// True if the change is only relevant to people working on the plugin. +Future _isDevChange(List pathComponents, + {GitVersionFinder? git, String? repoPath}) async { + return _isTestChange(pathComponents) || + // The top-level "tool" directory is for non-client-facing utility + // code, such as test scripts. + pathComponents.first == 'tool' || + // Ignoring lints doesn't affect clients. + pathComponents.contains('lint-baseline.xml') || + await _isGradleTestDependencyChange(pathComponents, + git: git, repoPath: repoPath); +} + +Future _isGradleTestDependencyChange(List pathComponents, + {GitVersionFinder? git, String? repoPath}) async { + if (git == null) { + return false; + } + if (pathComponents.last != 'build.gradle') { + return false; + } + final List diff = await git.getDiffContents(targetPath: repoPath); + final RegExp changeLine = RegExp(r'[+-] '); + final RegExp testDependencyLine = + RegExp(r'[+-]\s*(?:androidT|t)estImplementation\s'); + bool foundTestDependencyChange = false; + for (final String line in diff) { + if (!changeLine.hasMatch(line) || + line.startsWith('--- ') || + line.startsWith('+++ ')) { + continue; + } + if (!testDependencyLine.hasMatch(line)) { + return false; + } + foundTestDependencyChange = true; + } + // Only return true if a test dependency change was found, as a failsafe + // against having the wrong (e.g., incorrectly empty) diff output. + return foundTestDependencyChange; +} diff --git a/script/tool/lib/src/update_release_info_command.dart b/script/tool/lib/src/update_release_info_command.dart index b998615ead17..465b475eb9b5 100644 --- a/script/tool/lib/src/update_release_info_command.dart +++ b/script/tool/lib/src/update_release_info_command.dart @@ -133,7 +133,7 @@ class UpdateReleaseInfoCommand extends PackageLoopingCommand { packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = getRelativePosixPath(package.directory, from: gitRoot); - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: _changedFiles, relativePackagePath: relativePackagePath); diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 2e5f1efd7934..235611492b2d 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -18,8 +18,6 @@ import 'common/process_runner.dart'; import 'common/pub_version_finder.dart'; import 'common/repository_package.dart'; -const int _exitMissingChangeDescriptionFile = 3; - /// Categories of version change types. enum NextVersionType { /// A breaking change. @@ -116,11 +114,6 @@ class VersionCheckCommand extends PackageLoopingCommand { 'Defaults to false, which means the version check only run against ' 'the previous version in code.', ); - argParser.addOption(_changeDescriptionFile, - help: 'The path to a file containing the description of the change ' - '(e.g., PR description or commit message).\n\n' - 'If supplied, this is used to allow overrides to some version ' - 'checks.'); argParser.addOption(_prLabelsArg, help: 'A comma-separated list of labels associated with this PR, ' 'if applicable.\n\n' @@ -144,7 +137,6 @@ class VersionCheckCommand extends PackageLoopingCommand { } static const String _againstPubFlag = 'against-pub'; - static const String _changeDescriptionFile = 'change-description-file'; static const String _prLabelsArg = 'pr-labels'; static const String _checkForMissingChanges = 'check-for-missing-changes'; static const String _ignorePlatformInterfaceBreaks = @@ -171,7 +163,6 @@ class VersionCheckCommand extends PackageLoopingCommand { late final String _mergeBase; late final List _changedFiles; - late final String _changeDescription = _loadChangeDescription(); late final Set _prLabels = _getPRLabels(); @override @@ -519,21 +510,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return labels.split(',').map((String label) => label.trim()).toSet(); } - /// Returns the contents of the file pointed to by [_changeDescriptionFile], - /// or an empty string if that flag is not provided. - String _loadChangeDescription() { - final String path = getStringArg(_changeDescriptionFile); - if (path.isEmpty) { - return ''; - } - final File file = packagesDir.fileSystem.file(path); - if (!file.existsSync()) { - printError('${indentation}No such file: $path'); - throw ToolExit(_exitMissingChangeDescriptionFile); - } - return file.readAsStringSync(); - } - /// Returns true if the given version transition should be allowed. bool _shouldAllowVersionChange( {required Version oldVersion, required Version newVersion}) { @@ -569,8 +545,10 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final String relativePackagePath = getRelativePosixPath(package.directory, from: gitRoot); - final PackageChangeState state = checkPackageChangeState(package, - changedPaths: _changedFiles, relativePackagePath: relativePackagePath); + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: _changedFiles, + relativePackagePath: relativePackagePath, + git: await retrieveVersionFinder()); if (!state.hasChanges) { return null; @@ -580,9 +558,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. if (_prLabels.contains(_missingVersionChangeOverrideLabel)) { logWarning('Ignoring lack of version change due to the ' '"$_missingVersionChangeOverrideLabel" label.'); - } else if (_isAllowedDependabotChange(package, _changeDescription)) { - logWarning('Ignoring lack of version change for Dependabot change to ' - 'a known internal dependency.'); } else { printError( 'No version change found, but the change to this package could ' @@ -595,76 +570,23 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } } - if (!state.hasChangelogChange) { + if (!state.hasChangelogChange && state.needsChangelogChange) { if (_prLabels.contains(_missingChangelogChangeOverrideLabel)) { logWarning('Ignoring lack of CHANGELOG update due to the ' '"$_missingChangelogChangeOverrideLabel" label.'); - } else if (_isAllowedDependabotChange(package, _changeDescription)) { - logWarning('Ignoring lack of CHANGELOG update for Dependabot change to ' - 'a known internal dependency.'); } else { printError( 'No CHANGELOG change found. If this PR needs an exemption from ' 'the standard policy of listing all changes in the CHANGELOG, ' 'comment in the PR to explain why the PR is exempt, and add (or ' 'ask your reviewer to add) the ' - '"$_missingChangelogChangeOverrideLabel" label.'); + '"$_missingChangelogChangeOverrideLabel" label. Otherwise, ' + 'please add a NEXT entry in the CHANGELOG as described in ' + 'the contributing guide.'); return 'Missing CHANGELOG change'; } } return null; } - - /// Returns true if [changeDescription] matches a Dependabot change for a - /// dependency roll that should bypass the normal version and CHANGELOG change - /// checks (for dependencies that are known not to have client impact). - /// - /// Depending on CI, [changeDescription] may either be the PR description, or - /// the description of the last commit (see for example discussion in - /// https://github.com/cirruslabs/cirrus-ci-docs/issues/1029), so this needs - /// to handle both. - bool _isAllowedDependabotChange( - RepositoryPackage package, String changeDescription) { - // Espresso exports some dependencies that are normally just internal test - // utils, so always require reviewers to check that. - if (package.directory.basename == 'espresso') { - return false; - } - - // A string that is in all Dependabot PRs, but extremely unlikely to be in - // any other PR, to identify Dependabot PRs. - const String dependabotPRDescriptionMarker = - 'Dependabot commands and options'; - // The same thing, but for the Dependabot commit message. - const String dependabotCommitMessageMarker = - 'Signed-off-by: dependabot[bot]'; - // Expression to extract the name of the dependency being updated. - final RegExp dependencyRegex = - RegExp(r'Bumps? \[(.*?)\]\(.*?\) from [\d.]+ to [\d.]+'); - - // Allowed exact dependency names. - const Set allowedDependencies = { - 'junit', - 'robolectric', - }; - const Set allowedDependencyPrefixes = { - 'mockito-' // mockito-core, mockito-inline, etc. - }; - - if (changeDescription.contains(dependabotPRDescriptionMarker) || - changeDescription.contains(dependabotCommitMessageMarker)) { - final Match? match = dependencyRegex.firstMatch(changeDescription); - if (match != null) { - final String dependency = match.group(1)!; - if (allowedDependencies.contains(dependency) || - allowedDependencyPrefixes - .any((String prefix) => dependency.startsWith(prefix))) { - return true; - } - } - } - - return false; - } } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index b1d55e0fe550..e51c7433aa5c 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.9.3 +version: 0.10.0 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart index cc9116a9ea25..63ac1802e70c 100644 --- a/script/tool/test/common/package_state_utils_test.dart +++ b/script/tool/test/common/package_state_utils_test.dart @@ -3,7 +3,9 @@ // found in the LICENSE file. import 'package:file/file.dart'; import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:flutter_plugin_tools/src/common/package_state_utils.dart'; +import 'package:test/fake.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -26,12 +28,13 @@ void main() { 'packages/a_package/lib/plugin.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_package'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); test('handles trailing slash on package path', () async { @@ -42,16 +45,18 @@ void main() { 'packages/a_package/lib/plugin.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_package/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); expect(state.hasChangelogChange, false); }); - test('does not report version change exempt changes', () async { + test('does not flag version- and changelog-change-exempt changes', + () async { final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); @@ -64,12 +69,13 @@ void main() { 'packages/a_plugin/CHANGELOG.md', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, false); expect(state.hasChangelogChange, true); }); @@ -81,28 +87,49 @@ void main() { 'packages/a_plugin/lib/foo/tool/tool_thing.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); - test('requires a version change for example main', () async { - final RepositoryPackage package = - createFakePlugin('a_plugin', packagesDir); + test('requires a version change for example/lib/main.dart', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/lib/main.dart']); const List changedFiles = [ 'packages/a_plugin/example/lib/main.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test('requires a version change for example/main.dart', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/main.dart']); + + const List changedFiles = [ + 'packages/a_plugin/example/main.dart', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); test('requires a version change for example readme.md', () async { @@ -113,28 +140,189 @@ void main() { 'packages/a_plugin/example/README.md', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test('requires a version change for example/example.md', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/example.md']); + + const List changedFiles = [ + 'packages/a_plugin/example/example.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires a changelog change but no version change for ' + 'lower-priority examples when example.md is present', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/example.md']); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/main.dart', + 'packages/a_plugin/example/main.dart', + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires a changelog change but no version change for README.md when ' + 'code example is present', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/lib/main.dart']); + + const List changedFiles = [ + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, true); + }); + + test( + 'does not requires changelog or version change for build.gradle ' + 'test-dependency-only changes', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [ + "- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'", + "- testImplementation 'junit:junit:4.10.0'", + "+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'", + "+ testImplementation 'junit:junit:4.13.2'", + ] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, false); + }); + + test('requires changelog or version change for other build.gradle changes', + () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [ + "- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'", + "- testImplementation 'junit:junit:4.10.0'", + "+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'", + "+ testImplementation 'junit:junit:4.13.2'", + "- implementation 'com.google.android.gms:play-services-maps:18.0.0'", + "+ implementation 'com.google.android.gms:play-services-maps:18.0.2'", + ] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); - test('requires a version change for example example.md', () async { + test( + 'requires changelog or version change if build.gradle diffs cannot ' + 'be checked', () async { final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); const List changedFiles = [ - 'packages/a_plugin/example/lib/example.md', + 'packages/a_plugin/android/build.gradle', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires changelog or version change if build.gradle diffs cannot ' + 'be determined', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); }); } + +class FakeGitVersionFinder extends Fake implements GitVersionFinder { + FakeGitVersionFinder(this.fileDiffs); + + final Map> fileDiffs; + + @override + Future> getDiffContents({ + String? targetPath, + bool includeUncommitted = false, + }) async { + return fileDiffs[targetPath]!; + } +} diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index f8153750122a..2ff48d618258 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -40,72 +40,6 @@ void testAllowedVersion( } } -String _generateFakeDependabotPRDescription(String package) { - return ''' -Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. -
-Release notes -

Sourced from $package's releases.

-
-... -
-
-
-Commits -
    -
  • ...
  • -
-
-
- - -[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=$package&package-manager=gradle&previous-version=1.0.0&new-version=2.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) - -Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. - -[//]: # (dependabot-automerge-start) -[//]: # (dependabot-automerge-end) - ---- - -
-Dependabot commands and options -
- -You can trigger Dependabot actions by commenting on this PR: -- `@dependabot rebase` will rebase this PR -- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it -- `@dependabot merge` will merge this PR after your CI passes on it -- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it -- `@dependabot cancel merge` will cancel a previously requested merge and block automerging -- `@dependabot reopen` will reopen this PR if it is closed -- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually -- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) -- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) -- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - - -
-'''; -} - -String _generateFakeDependabotCommitMessage(String package) { - return ''' -Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. -- [Release notes](https://github.com/foo/$package/releases) -- [Commits](foo/$package@v4.3.1...v4.6.1) - ---- -updated-dependencies: -- dependency-name: $package - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] -'''; -} - class MockProcessResult extends Mock implements io.ProcessResult {} void main() { @@ -1085,242 +1019,45 @@ packages/plugin/example/lib/foo.dart ); }); - group('dependabot', () { - test('throws if a nonexistent change description file is specified', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - Error? commandError; - final List output = await _runWithMissingChangeDetection( - ['--change-description-file=a_missing_file.txt'], - errorHandler: (Error e) { - commandError = e; - }); - - expect(commandError, isA()); - expect( - output, - containsAllInOrder([ - contains('No such file: a_missing_file.txt'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for mockito', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('mockito-core')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for robolectric', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('robolectric')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for junit', () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile - .writeAsStringSync(_generateFakeDependabotPRDescription('junit')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('fails for dependencies that are not explicitly allowed', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); + // This test ensures that Dependabot Gradle changes to test-only files + // aren't flagged by the version check. + test( + 'allows missing CHANGELOG and version change for test-only Gradle changes', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); - const String changelog = ''' + const String changelog = ''' ## 1.0.0 * Some changes. '''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + // File list. + MockProcess(stdout: ''' packages/plugin/android/build.gradle '''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('somethingelse')); - - Error? commandError; - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ], errorHandler: (Error e) { - commandError = e; - }); - - expect(commandError, isA()); - expect( - output, - containsAllInOrder([ - contains('No version change found'), - contains('plugin:\n' - ' Missing version change'), - ]), - ); - }); - - test('allow list works for commit messages', () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle + // build.gradle diff + MockProcess(stdout: ''' +- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +- testImplementation 'junit:junit:4.10.0' ++ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' ++ testImplementation 'junit:junit:4.13.2' '''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotCommitMessage('mockito-core')); + ]; - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); + final List output = + await _runWithMissingChangeDetection([]); - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); }); }); From f2e11aa086a8d0d5a5da57568134e361fa01a864 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:18:10 +0000 Subject: [PATCH 03/49] [google_maps]: Bump espresso-core from 3.2.0 to 3.4.0 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6199) --- .../google_maps_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 54195fa40c1b..ecc85217c5bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'com.google.android.gms:play-services-maps:18.0.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'androidx.test:core:1.2.0' From 6edd4e6fe944ea58b96a327575862565ab5795ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:32:54 +0000 Subject: [PATCH 04/49] [gh_actions]: Bump github/codeql-action from 2.1.21 to 2.1.22 (#6362) --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 725eac6d6400..5f51be13913c 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c7f292ea4f542c473194b33813ccd4c207a6c725 + uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b with: sarif_file: results.sarif From c04e5a36b51ebe6257fc1c71c43895355419e95e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:34:10 +0000 Subject: [PATCH 05/49] [camera]: Bump robolectric from 4.5 to 4.8.2 in /packages/camera/camera_android/android (#6329) --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index b060181316c1..89156b602582 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -63,5 +63,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation 'org.robolectric:robolectric:4.5' + testImplementation 'org.robolectric:robolectric:4.8.2' } From 79194fed81d3e9f8634fefe52a618817c03d3b83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:34:13 +0000 Subject: [PATCH 06/49] [video_player]: Bump robolectric from 4.4 to 4.8.2 in /packages/video_player/video_player_android/example/android/app (#6326) --- .../video_player_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 84389f89e5e6..93caaa5c7c61 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -59,7 +59,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' - testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'org.robolectric:robolectric:4.8.2' testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From f032cdc44897ae480b2f7ec99eb6abcd076b7c4d Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Mon, 12 Sep 2022 11:44:12 +0200 Subject: [PATCH 07/49] [in_app_purchase] Replace `errorColor` to land deprecations (#6377) * Replace `errorColor` to land deprecations * x * x From 9abe69064a633a603482631f324c68cf22e75b2e Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 12 Sep 2022 07:33:32 -0700 Subject: [PATCH 08/49] Revert "[camera]: Bump robolectric from 4.5 to 4.8.2 in /packages/camera/camera_android/android (#6329)" (#6385) This reverts commit c04e5a36b51ebe6257fc1c71c43895355419e95e. --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 89156b602582..b060181316c1 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -63,5 +63,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation 'org.robolectric:robolectric:4.8.2' + testImplementation 'org.robolectric:robolectric:4.5' } From 3b8addccfbec216d4bedded3ec275fd067e0edb8 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 12 Sep 2022 11:37:16 -0400 Subject: [PATCH 09/49] [webview_flutter_wkwebview] Fixes typo in an internal method name, from `setCookieForInsances` to `setCookieForInstances` (#6384) --- .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 3 ++- .../webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart | 2 +- .../lib/src/web_kit/web_kit_api_impls.dart | 2 +- .../webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index fff0110dd4ea..056d1cb1c26a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Fixes typo in an internal method name, from `setCookieForInsances` to `setCookieForInstances`. ## 2.9.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index a671064ddc0f..566c46fda8bb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -385,7 +385,7 @@ class WKHttpCookieStore extends NSObject { /// Adds a cookie to the cookie store. Future setCookie(NSHttpCookie cookie) { - return _httpCookieStoreApi.setCookieForInsances(this, cookie); + return _httpCookieStoreApi.setCookieForInstances(this, cookie); } @override diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index 6a7fb6254889..614d0793e5f9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -481,7 +481,7 @@ class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { } /// Calls [setCookie] with the ids of the provided object instances. - Future setCookieForInsances( + Future setCookieForInstances( WKHttpCookieStore instance, NSHttpCookie cookie, ) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 804d4f3b12bc..40b7bf89601d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.9.3 +version: 2.9.4 environment: sdk: ">=2.17.0 <3.0.0" From 2fcecb109db51d18d2f32cdd4dcd502542f2427e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:35:53 +0000 Subject: [PATCH 10/49] [webview]: Bump mockito-inline from 4.7.0 to 4.8.0 in /packages/webview_flutter/webview_flutter_android/android (#6403) --- .../webview_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 8fd52e396da2..0b95a4be66ff 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.webkit:webkit:1.5.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'org.mockito:mockito-inline:4.8.0' testImplementation 'androidx.test:core:1.3.0' } From 2925683777816751e96e66e3310dca0d1856bb6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:36:31 +0000 Subject: [PATCH 11/49] [url_launcher]: Bump mockito-core from 1.10.19 to 4.8.0 in /packages/url_launcher/url_launcher_android/android (#6380) --- .../url_launcher/url_launcher_android/android/build.gradle | 2 +- .../plugins/urllauncher/MethodCallHandlerImplTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index a81080077997..a7d49e929a20 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -50,7 +50,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:1.10.19' + testImplementation 'org.mockito:mockito-core:4.8.0' testImplementation 'androidx.test:core:1.0.0' testImplementation 'org.robolectric:robolectric:4.3' } diff --git a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java index b60192531dbd..6bd88b650802 100644 --- a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java +++ b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java @@ -4,8 +4,8 @@ package io.flutter.plugins.urllauncher; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; From f6e933ba59fec68bca100bed9f3fb364593444d6 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 12 Sep 2022 12:18:35 -0700 Subject: [PATCH 12/49] Configure dependabot to ignore minor versions (#6355) --- .github/dependabot.yml | 246 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9a1011efaaba..16346f9a0b8c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera_android/example/android/app" @@ -15,6 +26,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera_android_camerax/android" @@ -23,6 +37,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera_android_camerax/example/android/app" @@ -31,6 +56,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera/example/android/app" @@ -39,6 +67,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/espresso/android" @@ -47,6 +78,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/espresso/example/android/app" @@ -55,6 +97,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/android" @@ -63,6 +108,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" @@ -71,6 +127,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" @@ -79,6 +138,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter_android/android" @@ -87,6 +149,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter_android/example/android/app" @@ -95,6 +168,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in/example/android/app" @@ -103,6 +179,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/android" @@ -111,6 +190,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" @@ -119,6 +209,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/android" @@ -127,6 +220,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" @@ -135,6 +239,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase/example/android/app" @@ -143,6 +250,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker/example/android/app" @@ -151,6 +261,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/android" @@ -159,6 +272,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/example/android/app" @@ -167,6 +291,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/android" @@ -175,6 +302,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/example/android/app" @@ -183,6 +321,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth/example/android/app" @@ -191,6 +332,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider/example/android/app" @@ -199,6 +343,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/android" @@ -207,6 +354,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/example/android/app" @@ -215,6 +373,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/android" @@ -223,6 +384,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/example/android/app" @@ -231,6 +403,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions/example/android/app" @@ -239,6 +414,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences/example/android/app" @@ -247,6 +425,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/android" @@ -255,6 +436,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" @@ -263,6 +455,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/android" @@ -271,6 +466,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/example/android/app" @@ -279,6 +485,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher/example/android/app" @@ -287,6 +496,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/video_player/video_player/example/android/app" @@ -295,6 +507,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/android" @@ -303,6 +518,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/example/android/app" @@ -311,6 +537,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter/example/android/app" @@ -319,6 +548,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/android" @@ -327,6 +559,17 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/example/android/app" @@ -335,6 +578,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "github-actions" directory: "/" From 4209dba5ec6f04e7f13e3ddcc718764e17865645 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:06:17 -0400 Subject: [PATCH 13/49] [webview_flutter] Implementation of the app facing WebViewWidget for v4 (#6367) --- .../lib/src/v4/src/webview_widget.dart | 64 +++++ .../lib/src/v4/webview_flutter.dart | 1 + .../test/v4/webview_widget_test.dart | 87 +++++++ .../test/v4/webview_widget_test.mocks.dart | 246 ++++++++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart new file mode 100644 index 000000000000..06e4f78028df --- /dev/null +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart @@ -0,0 +1,64 @@ +// 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/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_controller.dart'; + +/// Displays a native WebView as a Widget. +class WebViewWidget extends StatelessWidget { + /// Constructs a [WebViewWidget]. + WebViewWidget({ + Key? key, + required WebViewController controller, + TextDirection layoutDirection = TextDirection.ltr, + Set> gestureRecognizers = + const >{}, + }) : this.fromPlatformCreationParams( + key: key, + params: PlatformWebViewWidgetCreationParams( + controller: controller.platform, + layoutDirection: layoutDirection, + gestureRecognizers: gestureRecognizers, + ), + ); + + /// Constructs a [WebViewWidget] from creation params for a specific + /// platform. + WebViewWidget.fromPlatformCreationParams({ + Key? key, + required PlatformWebViewWidgetCreationParams params, + }) : this.fromPlatform(key: key, platform: PlatformWebViewWidget(params)); + + /// Constructs a [WebViewWidget] from a specific platform implementation. + WebViewWidget.fromPlatform({Key? key, required this.platform}) + : super(key: key); + + /// Implementation of [PlatformWebViewWidget] for the current platform. + final PlatformWebViewWidget platform; + + /// The layout direction to use for the embedded WebView. + late final TextDirection layoutDirection = platform.params.layoutDirection; + + /// Specifies which gestures should be consumed by the web view. + /// + /// It is possible for other gesture recognizers to be competing with the web + /// view on pointer events, e.g if the web view is inside a [ListView] the + /// [ListView] will want to handle vertical drags. The web view will claim + /// gestures that are recognized by any of the recognizers on this list. + /// + /// When `gestureRecognizers` is empty (default), the web view will only + /// handle pointer events for gestures that were not claimed by any other + /// gesture recognizer. + late final Set> gestureRecognizers = + platform.params.gestureRecognizers; + + @override + Widget build(BuildContext context) { + return platform.build(context); + } +} diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart index f3caf84dcf56..f4a0b207e27a 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart @@ -9,3 +9,4 @@ export 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_i export 'src/webview_controller.dart'; export 'src/webview_cookie_manager.dart'; +export 'src/webview_widget.dart'; diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart new file mode 100644 index 000000000000..455d8b371ec7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart @@ -0,0 +1,87 @@ +// 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/gestures.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter/src/v4/webview_flutter.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_widget_test.mocks.dart'; + +@GenerateMocks([PlatformWebViewController, PlatformWebViewWidget]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebViewWidget', () { + testWidgets('build', (WidgetTester tester) async { + final MockPlatformWebViewWidget mockPlatformWebViewWidget = + MockPlatformWebViewWidget(); + when(mockPlatformWebViewWidget.build(any)).thenReturn(Container()); + + await tester.pumpWidget(WebViewWidget.fromPlatform( + platform: mockPlatformWebViewWidget, + )); + + expect(find.byType(Container), findsOneWidget); + }); + + testWidgets( + 'constructor parameters are correctly passed to creation params', + (WidgetTester tester) async { + WebViewPlatform.instance = TestWebViewPlatform(); + + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + final WebViewController webViewController = + WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + final WebViewWidget webViewWidget = WebViewWidget( + key: GlobalKey(), + controller: webViewController, + layoutDirection: TextDirection.rtl, + gestureRecognizers: >{ + Factory(() => EagerGestureRecognizer()), + }, + ); + + // The key passed to the default constructor is used by the super class + // and not passed to the platform implementation. + expect(webViewWidget.platform.params.key, isNull); + expect( + webViewWidget.platform.params.controller, + webViewController.platform, + ); + expect(webViewWidget.platform.params.layoutDirection, TextDirection.rtl); + expect( + webViewWidget.platform.params.gestureRecognizers.isNotEmpty, + isTrue, + ); + }); + }); +} + +class TestWebViewPlatform extends WebViewPlatform { + @override + PlatformWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + return TestPlatformWebViewWidget(params); + } +} + +class TestPlatformWebViewWidget extends PlatformWebViewWidget { + TestPlatformWebViewWidget(PlatformWebViewWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart new file mode 100644 index 000000000000..e481d752be5d --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart @@ -0,0 +1,246 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in webview_flutter/test/v4/webview_widget_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i7; +import 'dart:math' as _i3; +import 'dart:ui' as _i9; + +import 'package:flutter/foundation.dart' as _i5; +import 'package:flutter/widgets.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i8; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart' + as _i6; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart' + as _i10; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart' + as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake + implements _i2.PlatformWebViewControllerCreationParams { + _FakePlatformWebViewControllerCreationParams_0( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePoint_1 extends _i1.SmartFake + implements _i3.Point { + _FakePoint_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePlatformWebViewWidgetCreationParams_2 extends _i1.SmartFake + implements _i2.PlatformWebViewWidgetCreationParams { + _FakePlatformWebViewWidgetCreationParams_2( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeWidget_3 extends _i1.SmartFake implements _i4.Widget { + _FakeWidget_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); + + @override + String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + super.toString(); +} + +/// A class which mocks [PlatformWebViewController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewController extends _i1.Mock + implements _i6.PlatformWebViewController { + MockPlatformWebViewController() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewControllerCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewControllerCreationParams_0( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewControllerCreationParams); + @override + _i7.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( + Invocation.method(#loadFile, [absoluteFilePath]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future loadFlutterAsset(String? key) => (super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [key]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future loadHtmlString(String? html, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future loadRequest(_i2.LoadRequestParams? params) => + (super.noSuchMethod(Invocation.method(#loadRequest, [params]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future currentUrl() => + (super.noSuchMethod(Invocation.method(#currentUrl, []), + returnValue: _i7.Future.value()) as _i7.Future); + @override + _i7.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: _i7.Future.value(false)) as _i7.Future); + @override + _i7.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: _i7.Future.value(false)) as _i7.Future); + @override + _i7.Future goBack() => (super.noSuchMethod( + Invocation.method(#goBack, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future goForward() => (super.noSuchMethod( + Invocation.method(#goForward, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future reload() => (super.noSuchMethod( + Invocation.method(#reload, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future clearCache() => (super.noSuchMethod( + Invocation.method(#clearCache, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future clearLocalStorage() => (super.noSuchMethod( + Invocation.method(#clearLocalStorage, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setPlatformNavigationDelegate( + _i8.PlatformNavigationDelegate? handler) => + (super.noSuchMethod( + Invocation.method(#setPlatformNavigationDelegate, [handler]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future runJavaScript(String? javaScript) => (super.noSuchMethod( + Invocation.method(#runJavaScript, [javaScript]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future runJavaScriptReturningResult(String? javaScript) => + (super.noSuchMethod( + Invocation.method(#runJavaScriptReturningResult, [javaScript]), + returnValue: _i7.Future.value('')) as _i7.Future); + @override + _i7.Future addJavaScriptChannel( + _i6.JavaScriptChannelParams? javaScriptChannelParams) => + (super.noSuchMethod( + Invocation.method(#addJavaScriptChannel, [javaScriptChannelParams]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: + _i7.Future.value()) as _i7.Future); + @override + _i7.Future removeJavaScriptChannel(String? javaScriptChannelName) => + (super.noSuchMethod( + Invocation.method(#removeJavaScriptChannel, [javaScriptChannelName]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: + _i7.Future.value()) as _i7.Future); + @override + _i7.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: _i7.Future.value()) as _i7.Future); + @override + _i7.Future scrollTo(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollTo, [x, y]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future scrollBy(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollBy, [x, y]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future<_i3.Point> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: _i7.Future<_i3.Point>.value(_FakePoint_1( + this, Invocation.method(#getScrollPosition, [])))) + as _i7.Future<_i3.Point>); + @override + _i7.Future enableDebugging(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableDebugging, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future enableGestureNavigation(bool? enabled) => (super + .noSuchMethod(Invocation.method(#enableGestureNavigation, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future enableZoom(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableZoom, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setBackgroundColor(_i9.Color? color) => (super.noSuchMethod( + Invocation.method(#setBackgroundColor, [color]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) => + (super.noSuchMethod( + Invocation.method(#setJavaScriptMode, [javaScriptMode]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future setUserAgent(String? userAgent) => (super.noSuchMethod( + Invocation.method(#setUserAgent, [userAgent]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); +} + +/// A class which mocks [PlatformWebViewWidget]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewWidget extends _i1.Mock + implements _i10.PlatformWebViewWidget { + MockPlatformWebViewWidget() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewWidgetCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewWidgetCreationParams_2( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewWidgetCreationParams); + @override + _i4.Widget build(_i4.BuildContext? context) => + (super.noSuchMethod(Invocation.method(#build, [context]), + returnValue: + _FakeWidget_3(this, Invocation.method(#build, [context]))) + as _i4.Widget); +} From 35084f139c15c89cc45e9cb0199d2b212090f0fa Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Mon, 12 Sep 2022 14:15:55 -0700 Subject: [PATCH 14/49] Revert "[url_launcher]: Bump gradle from 3.4.2 to 7.2.2 in /packages/url_launcher/url_launcher_android/android #6194" (#6383) --- packages/url_launcher/url_launcher_android/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_android/android/build.gradle | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index be98dc2ddc5e..c75e93e821a1 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.19 + +* Revert gradle back to 3.4.2. + ## 6.0.18 * Updates gradle to 7.2.2. diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index a7d49e929a20..a4fa269cbfc7 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:3.4.2' } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 2b0ba2e1b75e..db2bddcc54c3 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.18 +version: 6.0.19 environment: sdk: ">=2.14.0 <3.0.0" From 0497c90841ea31cd0a493ff6e0fa896dbcc0215e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Sep 2022 11:00:45 -0400 Subject: [PATCH 15/49] Roll Flutter (stable) from ffccd96b62ee to 4f9d92fbbdf0 (1 revision) --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 14a210a6a125..d9b146dd7f87 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -ffccd96b62ee8cec7740dab303538c5fc26ac543 +4f9d92fbbdf072a70a70d2179a9f87392b94104c From 7e65cdd1d4079bd2bf8034952a7a7b50fca082d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 11:02:30 -0400 Subject: [PATCH 16/49] [image_picker]: Bump mockito-core from 3.10.0 to 4.8.0 in /packages/image_picker/image_picker_android/android (#6381) * [image_picker]: Bump mockito-core Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update tests for API changes * format Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stuart Morgan --- .../image_picker_android/android/build.gradle | 2 +- .../imagepicker/ImagePickerPluginTest.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 1d428462bea3..a6c01e386448 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.exifinterface:exifinterface:1.3.3' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.10.0' + testImplementation 'org.mockito:mockito-core:4.8.0' testImplementation 'androidx.test:core:1.4.0' testImplementation "org.robolectric:robolectric:4.8.1" } diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java index ce41343e3d2c..36452479776e 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.app.Activity; @@ -76,7 +76,7 @@ public void onMethodCall_WhenActivityIsNull_FinishesWithForegroundActivityRequir imagePickerPluginWithNullActivity.onMethodCall(call, mockResult); verify(mockResult) .error("no_activity", "image_picker plugin requires a foreground activity.", null); - verifyZeroInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockImagePickerDelegate); } @Test @@ -84,8 +84,8 @@ public void onMethodCall_WhenCalledWithUnknownMethod_ThrowsException() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Unknown method test"); plugin.onMethodCall(new MethodCall("test", null), mockResult); - verifyZeroInteractions(mockImagePickerDelegate); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockResult); } @Test @@ -93,8 +93,8 @@ public void onMethodCall_WhenCalledWithUnknownImageSource_ThrowsException() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Invalid image source: -1"); plugin.onMethodCall(buildMethodCall(PICK_IMAGE, -1), mockResult); - verifyZeroInteractions(mockImagePickerDelegate); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockResult); } @Test @@ -102,7 +102,7 @@ public void onMethodCall_WhenSourceIsGallery_InvokesChooseImageFromGallery() { MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_GALLERY); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).chooseImageFromGallery(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test @@ -110,7 +110,7 @@ public void onMethodCall_InvokesChooseMultiImageFromGallery() { MethodCall call = buildMethodCall(PICK_MULTI_IMAGE); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).chooseMultiImageFromGallery(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test @@ -118,7 +118,7 @@ public void onMethodCall_WhenSourceIsCamera_InvokesTakeImageWithCamera() { MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).takeImageWithCamera(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test From 3cf0ec5f262c1b727df7d3f72580284d1c2b8920 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 13 Sep 2022 09:25:02 -0700 Subject: [PATCH 17/49] [ci] Manually roll master (#6418) Rolls Flutter master to the version that first failed due to flutter/engine#33688 Updates the camera_windows mocks to include the new method so that the tests will compile on master. Fixes the blocked roll. --- .ci/flutter_master.version | 2 +- .../windows/test/capture_controller_test.cpp | 4 ++-- .../camera_windows/windows/test/mocks.h | 21 ++++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index af8df44327ff..74c1c9a9cd2b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4cd734ae3a1cc0c8f132ba07e6d2e0a3253c53e7 +4930444f4afe85272bbdc84b43196d59dad71166 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 4662d3340456..8d6632cbc3f0 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -291,7 +291,7 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { 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(*texture_registrar, UnregisterTexture(_)).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineFailed(Eq(CameraResult::kError), @@ -335,7 +335,7 @@ TEST(CaptureController, InitCaptureEngineReportsAccessDenied) { .WillOnce(Return(E_ACCESSDENIED)); EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); - EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture(_)).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineFailed(Eq(CameraResult::kAccessDenied), diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 678b75899bc2..b6416eb7c710 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -67,7 +67,8 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { return this->texture_id_; }); - ON_CALL(*this, UnregisterTexture) + // Deprecated pre-Flutter-3.4 version. + ON_CALL(*this, UnregisterTexture(_)) .WillByDefault([this](int64_t tid) -> bool { if (tid == this->texture_id_) { texture_ = nullptr; @@ -77,6 +78,18 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { return false; }); + // Flutter 3.4+ version. + ON_CALL(*this, UnregisterTexture(_, _)) + .WillByDefault( + [this](int64_t tid, std::function callback) -> void { + // Forward to the pre-3.4 implementation so that expectations can + // be the same for all versions. + this->UnregisterTexture(tid); + if (callback) { + callback(); + } + }); + ON_CALL(*this, MarkTextureFrameAvailable) .WillByDefault([this](int64_t tid) -> bool { if (tid == this->texture_id_) { @@ -91,7 +104,13 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { MOCK_METHOD(int64_t, RegisterTexture, (flutter::TextureVariant * texture), (override)); + // Pre-Flutter-3.4 version. MOCK_METHOD(bool, UnregisterTexture, (int64_t), (override)); + // Flutter 3.4+ version. + // TODO(cbracken): Add an override annotation to this once 3.4+ is the + // minimum version tested in CI. + MOCK_METHOD(void, UnregisterTexture, + (int64_t, std::function callback), ()); MOCK_METHOD(bool, MarkTextureFrameAvailable, (int64_t), (override)); int64_t texture_id_ = -1; From 0fca680fbca9cf6a9ace2e94e42c4f8bd8de11e6 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:12:57 -0400 Subject: [PATCH 18/49] [webview_flutter_android] Adds the scroll position method to WebView and updates pigeon instructions (#6366) --- .../webview_flutter_android/CHANGELOG.md | 5 + .../webview_flutter_android/README.md | 17 +- .../GeneratedAndroidWebView.java | 125 ++++++++- .../webviewflutter/WebViewHostApiImpl.java | 11 + .../plugins/webviewflutter/WebViewTest.java | 9 + .../lib/src/android_webview.dart | 5 + .../lib/src/android_webview.pigeon.dart | 83 +++++- .../lib/src/android_webview_api_impls.dart | 10 +- .../pigeons/android_webview.dart | 9 + .../webview_flutter_android/pubspec.yaml | 6 +- .../test/android_webview_test.dart | 9 + .../test/android_webview_test.mocks.dart | 165 ++++++------ .../test/test_android_webview.pigeon.dart | 49 +++- .../webview_android_widget_test.mocks.dart | 237 +++++++++--------- 14 files changed, 518 insertions(+), 222 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 0eb1aac530df..910d20dda6d1 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.10.1 + +* Adds a method to the `WebView` wrapper to retrieve the X and Y positions simultaneously. +* Removes reference to https://github.com/flutter/flutter/issues/97744 from `README`. + ## 2.10.0 * Bumps webkit from 1.0.0 to 1.5.0. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 80f1f6e0b9cb..bdd9edf99eaa 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -14,25 +14,10 @@ platform (Android). The communication interface is defined in the `pigeons/andro file. After editing the communication interface regenerate the communication layer by running `flutter pub run pigeon --input pigeons/android_webview.dart`. -Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test -pigeon file needs one of its imports updated to properly work with `mockito`. - -In `test/android_webview.pigeon.dart`, change - -```dart -import '../lib/src/android_webview.pigeon.dart'; -``` - -to - -```dart -import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; -``` - Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: ```bash -flutter packages pub run build_runner build --delete-conflicting-outputs +flutter pub run build_runner build --delete-conflicting-outputs ``` If you would like to contribute to the plugin, check out our [contribution guide][5]. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 34fd8b79b315..9a6b1c44d39d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -1,7 +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. -// Autogenerated from Pigeon (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; @@ -265,6 +265,78 @@ Map toMap() { } } + /** Generated class from Pigeon that represents data sent in messages. */ + public static class WebViewPoint { + private @NonNull Long x; + + public @NonNull Long getX() { + return x; + } + + public void setX(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"x\" is null."); + } + this.x = setterArg; + } + + private @NonNull Long y; + + public @NonNull Long getY() { + return y; + } + + public void setY(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"y\" is null."); + } + this.y = setterArg; + } + + /** Constructor is private to enforce null safety; use Builder. */ + private WebViewPoint() {} + + public static final class Builder { + private @Nullable Long x; + + public @NonNull Builder setX(@NonNull Long setterArg) { + this.x = setterArg; + return this; + } + + private @Nullable Long y; + + public @NonNull Builder setY(@NonNull Long setterArg) { + this.y = setterArg; + return this; + } + + public @NonNull WebViewPoint build() { + WebViewPoint pigeonReturn = new WebViewPoint(); + pigeonReturn.setX(x); + pigeonReturn.setY(y); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); + toMapResult.put("x", x); + toMapResult.put("y", y); + return toMapResult; + } + + static @NonNull WebViewPoint fromMap(@NonNull Map map) { + WebViewPoint pigeonResult = new WebViewPoint(); + Object x = map.get("x"); + pigeonResult.setX((x == null) ? null : ((x instanceof Integer) ? (Integer) x : (Long) x)); + Object y = map.get("y"); + pigeonResult.setY((y == null) ? null : ((y instanceof Integer) ? (Integer) y : (Long) y)); + return pigeonResult; + } + } + public interface Result { void success(T result); @@ -444,6 +516,27 @@ private static class WebViewHostApiCodec extends StandardMessageCodec { public static final WebViewHostApiCodec INSTANCE = new WebViewHostApiCodec(); private WebViewHostApiCodec() {} + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return WebViewPoint.fromMap((Map) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof WebViewPoint) { + stream.write(128); + writeValue(stream, ((WebViewPoint) value).toMap()); + } else { + super.writeValue(stream, value); + } + } } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ @@ -504,6 +597,9 @@ void evaluateJavascript( @NonNull Long getScrollY(@NonNull Long instanceId); + @NonNull + WebViewPoint getScrollPosition(@NonNull Long instanceId); + void setWebContentsDebuggingEnabled(@NonNull Boolean enabled); void setWebViewClient(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); @@ -1108,6 +1204,33 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.getScrollPosition", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + WebViewPoint output = + api.getScrollPosition( + (instanceIdArg == null) ? null : instanceIdArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index 56761d100133..f257ace71b0d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -21,6 +21,7 @@ import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.ReleasableWebViewClient; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Host api implementation for {@link WebView}. @@ -456,6 +457,16 @@ public Long getScrollY(Long instanceId) { return (long) webView.getScrollY(); } + @NonNull + @Override + public GeneratedAndroidWebView.WebViewPoint getScrollPosition(@NonNull Long instanceId) { + final WebView webView = Objects.requireNonNull(instanceManager.getInstance(instanceId)); + return new GeneratedAndroidWebView.WebViewPoint.Builder() + .setX((long) webView.getScrollX()) + .setY((long) webView.getScrollY()) + .build(); + } + @Override public void setWebContentsDebuggingEnabled(Boolean enabled) { webViewProxy.setWebContentsDebuggingEnabled(enabled); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java index 89bbd7c6c7c0..30bc256cd985 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java @@ -312,6 +312,15 @@ public void getScrollY() { assertEquals((long) testHostApiImpl.getScrollY(0L), 23); } + @Test + public void getScrollPosition() { + when(mockWebView.getScrollX()).thenReturn(1); + when(mockWebView.getScrollY()).thenReturn(2); + final GeneratedAndroidWebView.WebViewPoint position = testHostApiImpl.getScrollPosition(0L); + assertEquals((long) position.getX(), 1L); + assertEquals((long) position.getY(), 2L); + } + @Test public void setWebViewClient() { final WebViewClient mockWebViewClient = mock(WebViewClient.class); 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 30c59ea43d2c..e08703106990 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 @@ -321,6 +321,11 @@ class WebView extends JavaObject { return api.getScrollYFromInstance(this); } + /// Returns the X and Y scroll position of this view. + Future getScrollPosition() { + return api.getScrollPositionFromInstance(this); + } + /// Sets the [WebViewClient] that will receive various notifications and requests. /// /// This will replace the current handler. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index f7c83b79d25a..e793dbb950a8 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,14 +1,13 @@ // 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. -// Autogenerated from Pigeon (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name -// @dart = 2.12 +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; class WebResourceRequestData { @@ -78,6 +77,31 @@ class WebResourceErrorData { } } +class WebViewPoint { + WebViewPoint({ + required this.x, + required this.y, + }); + + int x; + int y; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['x'] = x; + pigeonMap['y'] = y; + return pigeonMap; + } + + static WebViewPoint decode(Object message) { + final Map pigeonMap = message as Map; + return WebViewPoint( + x: pigeonMap['x']! as int, + y: pigeonMap['y']! as int, + ); + } +} + class _JavaObjectHostApiCodec extends StandardMessageCodec { const _JavaObjectHostApiCodec(); } @@ -221,6 +245,26 @@ class CookieManagerHostApi { class _WebViewHostApiCodec extends StandardMessageCodec { const _WebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WebViewPoint) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WebViewPoint.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } class WebViewHostApi { @@ -734,6 +778,35 @@ class WebViewHostApi { } } + Future getScrollPosition(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as WebViewPoint?)!; + } + } + Future setWebContentsDebuggingEnabled(bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', 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 00ededabd977..4f2c51c4a13a 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 @@ -5,8 +5,9 @@ // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) // ignore: unnecessary_import import 'dart:typed_data'; +import 'dart:ui'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show BinaryMessenger; import 'android_webview.dart'; import 'android_webview.pigeon.dart'; @@ -278,6 +279,13 @@ class WebViewHostApiImpl extends WebViewHostApi { return getScrollY(instanceManager.getIdentifier(instance)!); } + /// Helper method to convert instances ids to objects. + Future getScrollPositionFromInstance(WebView instance) async { + final WebViewPoint position = + await getScrollPosition(instanceManager.getIdentifier(instance)!); + return Offset(position.x.toDouble(), position.y.toDouble()); + } + /// Helper method to convert instances ids to objects. Future setWebViewClientFromInstance( WebView instance, diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index eb979870a1d3..f3946ed4212a 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -51,6 +51,13 @@ class WebResourceErrorData { String description; } +class WebViewPoint { + WebViewPoint(this.x, this.y); + + int x; + int y; +} + /// Handles methods calls to the native Java Object class. /// /// Also handles calls to remove the reference to an instance with `dispose`. @@ -141,6 +148,8 @@ abstract class WebViewHostApi { int getScrollY(int instanceId); + WebViewPoint getScrollPosition(int instanceId); + void setWebContentsDebuggingEnabled(bool enabled); void setWebViewClient(int instanceId, int webViewClientInstanceId); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 4c1f5766e712..62f4cc97aa06 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.10.0 +version: 2.10.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -27,5 +27,5 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.1.0 - pigeon: ^3.0.3 + mockito: ^5.2.0 + pigeon: ^4.0.2 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 d57eadc1d5cc..f3ec4bd0cb9f 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 @@ -252,6 +252,15 @@ void main() { expect(webView.getScrollY(), completion(56)); }); + test('getScrollPosition', () async { + when(mockPlatformHostApi.getScrollPosition(webViewInstanceId)) + .thenReturn(WebViewPoint(x: 2, y: 16)); + await expectLater( + webView.getScrollPosition(), + completion(const Offset(2.0, 16.0)), + ); + }); + test('setWebViewClient', () { TestWebViewClientHostApi.setup(MockTestWebViewClientHostApi()); WebViewClient.api = WebViewClientHostApiImpl( 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 6afda598d93b..116ac834f1d6 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 @@ -2,15 +2,15 @@ // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; -import 'dart:typed_data' as _i6; -import 'dart:ui' as _i7; +import 'dart:async' as _i5; +import 'dart:typed_data' as _i7; +import 'dart:ui' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; -import 'test_android_webview.pigeon.dart' as _i5; +import 'test_android_webview.pigeon.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -28,13 +28,17 @@ class _FakeDownloadListener_0 extends _i1.Fake class _FakeJavaScriptChannel_1 extends _i1.Fake implements _i2.JavaScriptChannel {} -class _FakeWebChromeClient_2 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebViewPoint_2 extends _i1.Fake implements _i3.WebViewPoint {} -class _FakeWebSettings_3 extends _i1.Fake implements _i2.WebSettings {} +class _FakeWebChromeClient_3 extends _i1.Fake implements _i2.WebChromeClient {} -class _FakeWebView_4 extends _i1.Fake implements _i2.WebView {} +class _FakeWebSettings_4 extends _i1.Fake implements _i2.WebSettings {} -class _FakeWebViewClient_5 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeOffset_5 extends _i1.Fake implements _i4.Offset {} + +class _FakeWebView_6 extends _i1.Fake implements _i2.WebView {} + +class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} /// A class which mocks [CookieManagerHostApi]. /// @@ -46,14 +50,14 @@ class MockCookieManagerHostApi extends _i1.Mock } @override - _i4.Future clearCookies() => + _i5.Future clearCookies() => (super.noSuchMethod(Invocation.method(#clearCookies, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future setCookie(String? arg_url, String? arg_value) => + _i5.Future setCookie(String? arg_url, String? arg_value) => (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [DownloadListener]. @@ -103,7 +107,7 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { /// /// See the documentation for Mockito's code generation for more information. class MockTestDownloadListenerHostApi extends _i1.Mock - implements _i5.TestDownloadListenerHostApi { + implements _i6.TestDownloadListenerHostApi { MockTestDownloadListenerHostApi() { _i1.throwOnMissingStub(this); } @@ -118,7 +122,7 @@ class MockTestDownloadListenerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestJavaObjectHostApi extends _i1.Mock - implements _i5.TestJavaObjectHostApi { + implements _i6.TestJavaObjectHostApi { MockTestJavaObjectHostApi() { _i1.throwOnMissingStub(this); } @@ -133,7 +137,7 @@ class MockTestJavaObjectHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestJavaScriptChannelHostApi extends _i1.Mock - implements _i5.TestJavaScriptChannelHostApi { + implements _i6.TestJavaScriptChannelHostApi { MockTestJavaScriptChannelHostApi() { _i1.throwOnMissingStub(this); } @@ -148,7 +152,7 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebChromeClientHostApi extends _i1.Mock - implements _i5.TestWebChromeClientHostApi { + implements _i6.TestWebChromeClientHostApi { MockTestWebChromeClientHostApi() { _i1.throwOnMissingStub(this); } @@ -164,7 +168,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebSettingsHostApi extends _i1.Mock - implements _i5.TestWebSettingsHostApi { + implements _i6.TestWebSettingsHostApi { MockTestWebSettingsHostApi() { _i1.throwOnMissingStub(this); } @@ -240,7 +244,7 @@ class MockTestWebSettingsHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebStorageHostApi extends _i1.Mock - implements _i5.TestWebStorageHostApi { + implements _i6.TestWebStorageHostApi { MockTestWebStorageHostApi() { _i1.throwOnMissingStub(this); } @@ -259,7 +263,7 @@ class MockTestWebStorageHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewClientHostApi extends _i1.Mock - implements _i5.TestWebViewClientHostApi { + implements _i6.TestWebViewClientHostApi { MockTestWebViewClientHostApi() { _i1.throwOnMissingStub(this); } @@ -275,7 +279,7 @@ class MockTestWebViewClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewHostApi extends _i1.Mock - implements _i5.TestWebViewHostApi { + implements _i6.TestWebViewHostApi { MockTestWebViewHostApi() { _i1.throwOnMissingStub(this); } @@ -308,7 +312,7 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#loadUrl, [instanceId, url, headers]), returnValueForMissingStub: null); @override - void postUrl(int? instanceId, String? url, _i6.Uint8List? data) => + void postUrl(int? instanceId, String? url, _i7.Uint8List? data) => super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), returnValueForMissingStub: null); @override @@ -340,12 +344,12 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavascript( + _i5.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override String? getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) @@ -367,6 +371,10 @@ class MockTestWebViewHostApi extends _i1.Mock (super.noSuchMethod(Invocation.method(#getScrollY, [instanceId]), returnValue: 0) as int); @override + _i3.WebViewPoint getScrollPosition(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getScrollPosition, [instanceId]), + returnValue: _FakeWebViewPoint_2()) as _i3.WebViewPoint); + @override void setWebContentsDebuggingEnabled(bool? enabled) => super.noSuchMethod( Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValueForMissingStub: null); @@ -412,7 +420,7 @@ class MockTestWebViewHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestAssetManagerHostApi extends _i1.Mock - implements _i5.TestAssetManagerHostApi { + implements _i6.TestAssetManagerHostApi { MockTestAssetManagerHostApi() { _i1.throwOnMissingStub(this); } @@ -442,7 +450,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { @override _i2.WebChromeClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_2()) as _i2.WebChromeClient); + returnValue: _FakeWebChromeClient_3()) as _i2.WebChromeClient); } /// A class which mocks [WebView]. @@ -460,17 +468,17 @@ class MockWebView extends _i1.Mock implements _i2.WebView { @override _i2.WebSettings get settings => (super.noSuchMethod(Invocation.getter(#settings), - returnValue: _FakeWebSettings_3()) as _i2.WebSettings); + returnValue: _FakeWebSettings_4()) as _i2.WebSettings); @override - _i4.Future loadData( + _i5.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadDataWithBaseUrl( + _i5.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -485,117 +493,122 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadUrl(String? url, Map? headers) => + _i5.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future postUrl(String? url, _i6.Uint8List? data) => + _i5.Future postUrl(String? url, _i7.Uint8List? data) => (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future clearCache(bool? includeDiskFiles) => + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future evaluateJavascript(String? javascriptString) => (super + _i5.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future scrollTo(int? x, int? y) => + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future scrollBy(int? x, int? y) => + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getScrollX() => + _i5.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future getScrollY() => + _i5.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); + @override + _i5.Future<_i4.Offset> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: Future<_i4.Offset>.value(_FakeOffset_5())) + as _i5.Future<_i4.Offset>); @override - _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future addJavaScriptChannel( + _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future removeJavaScriptChannel( + _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDownloadListener(_i2.DownloadListener? listener) => + _i5.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBackgroundColor(_i7.Color? color) => + _i5.Future setBackgroundColor(_i4.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future release() => + _i5.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_4()) as _i2.WebView); + returnValue: _FakeWebView_6()) as _i2.WebView); } /// A class which mocks [WebViewClient]. @@ -641,5 +654,5 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { returnValueForMissingStub: null); @override _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_5()) as _i2.WebViewClient); + returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); } diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index 88429fdb337e..afc80ab53b41 100644 --- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,14 +1,13 @@ // 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. -// Autogenerated from Pigeon (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports -// @dart = 2.12 import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; -import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -48,6 +47,26 @@ abstract class TestJavaObjectHostApi { class _TestWebViewHostApiCodec extends StandardMessageCodec { const _TestWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WebViewPoint) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WebViewPoint.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } abstract class TestWebViewHostApi { @@ -74,6 +93,7 @@ abstract class TestWebViewHostApi { void scrollBy(int instanceId, int x, int y); int getScrollX(int instanceId); int getScrollY(int instanceId); + WebViewPoint getScrollPosition(int instanceId); void setWebContentsDebuggingEnabled(bool enabled); void setWebViewClient(int instanceId, int webViewClientInstanceId); void addJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); @@ -492,6 +512,25 @@ abstract class TestWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition was null, expected non-null int.'); + final WebViewPoint output = api.getScrollPosition(arg_instanceId!); + return {'result': output}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', 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 e17b31b3d857..acaff1c0af8b 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 @@ -2,15 +2,15 @@ // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; -import 'dart:typed_data' as _i5; -import 'dart:ui' as _i6; +import 'dart:async' as _i5; +import 'dart:typed_data' as _i6; +import 'dart:ui' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/webview_android_widget.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i3; + as _i4; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -26,20 +26,22 @@ class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {} -class _FakeWebView_2 extends _i1.Fake implements _i2.WebView {} +class _FakeOffset_2 extends _i1.Fake implements _i3.Offset {} -class _FakeDownloadListener_3 extends _i1.Fake +class _FakeWebView_3 extends _i1.Fake implements _i2.WebView {} + +class _FakeDownloadListener_4 extends _i1.Fake implements _i2.DownloadListener {} -class _FakeJavascriptChannelRegistry_4 extends _i1.Fake - implements _i3.JavascriptChannelRegistry {} +class _FakeJavascriptChannelRegistry_5 extends _i1.Fake + implements _i4.JavascriptChannelRegistry {} -class _FakeJavaScriptChannel_5 extends _i1.Fake +class _FakeJavaScriptChannel_6 extends _i1.Fake implements _i2.JavaScriptChannel {} -class _FakeWebChromeClient_6 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebChromeClient_7 extends _i1.Fake implements _i2.WebChromeClient {} -class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeWebViewClient_8 extends _i1.Fake implements _i2.WebViewClient {} /// A class which mocks [FlutterAssetManager]. /// @@ -51,14 +53,14 @@ class MockFlutterAssetManager extends _i1.Mock } @override - _i4.Future> list(String? path) => + _i5.Future> list(String? path) => (super.noSuchMethod(Invocation.method(#list, [path]), returnValue: Future>.value([])) - as _i4.Future>); + as _i5.Future>); @override - _i4.Future getAssetFilePathByName(String? name) => + _i5.Future getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), - returnValue: Future.value('')) as _i4.Future); + returnValue: Future.value('')) as _i5.Future); } /// A class which mocks [WebSettings]. @@ -70,67 +72,67 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { } @override - _i4.Future setDomStorageEnabled(bool? flag) => + _i5.Future setDomStorageEnabled(bool? flag) => (super.noSuchMethod(Invocation.method(#setDomStorageEnabled, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setJavaScriptCanOpenWindowsAutomatically(bool? flag) => + _i5.Future setJavaScriptCanOpenWindowsAutomatically(bool? flag) => (super.noSuchMethod( Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setSupportMultipleWindows(bool? support) => (super + _i5.Future setSupportMultipleWindows(bool? support) => (super .noSuchMethod(Invocation.method(#setSupportMultipleWindows, [support]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setJavaScriptEnabled(bool? flag) => + _i5.Future setJavaScriptEnabled(bool? flag) => (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setUserAgentString(String? userAgentString) => (super + _i5.Future setUserAgentString(String? userAgentString) => (super .noSuchMethod(Invocation.method(#setUserAgentString, [userAgentString]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setMediaPlaybackRequiresUserGesture(bool? require) => + _i5.Future setMediaPlaybackRequiresUserGesture(bool? require) => (super.noSuchMethod( Invocation.method(#setMediaPlaybackRequiresUserGesture, [require]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setSupportZoom(bool? support) => + _i5.Future setSupportZoom(bool? support) => (super.noSuchMethod(Invocation.method(#setSupportZoom, [support]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setLoadWithOverviewMode(bool? overview) => (super + _i5.Future setLoadWithOverviewMode(bool? overview) => (super .noSuchMethod(Invocation.method(#setLoadWithOverviewMode, [overview]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setUseWideViewPort(bool? use) => + _i5.Future setUseWideViewPort(bool? use) => (super.noSuchMethod(Invocation.method(#setUseWideViewPort, [use]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDisplayZoomControls(bool? enabled) => + _i5.Future setDisplayZoomControls(bool? enabled) => (super.noSuchMethod(Invocation.method(#setDisplayZoomControls, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBuiltInZoomControls(bool? enabled) => + _i5.Future setBuiltInZoomControls(bool? enabled) => (super.noSuchMethod(Invocation.method(#setBuiltInZoomControls, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setAllowFileAccess(bool? enabled) => + _i5.Future setAllowFileAccess(bool? enabled) => (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebSettings copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @@ -145,10 +147,10 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { } @override - _i4.Future deleteAllData() => + _i5.Future deleteAllData() => (super.noSuchMethod(Invocation.method(#deleteAllData, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebStorage copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeWebStorage_1()) as _i2.WebStorage); @@ -171,15 +173,15 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override - _i4.Future loadData( + _i5.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadDataWithBaseUrl( + _i5.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -194,117 +196,122 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadUrl(String? url, Map? headers) => + _i5.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future postUrl(String? url, _i5.Uint8List? data) => + _i5.Future postUrl(String? url, _i6.Uint8List? data) => (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future clearCache(bool? includeDiskFiles) => + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future evaluateJavascript(String? javascriptString) => (super + _i5.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future scrollTo(int? x, int? y) => + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future scrollBy(int? x, int? y) => + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getScrollX() => + _i5.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future getScrollY() => + _i5.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); + @override + _i5.Future<_i3.Offset> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: Future<_i3.Offset>.value(_FakeOffset_2())) + as _i5.Future<_i3.Offset>); @override - _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future addJavaScriptChannel( + _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future removeJavaScriptChannel( + _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDownloadListener(_i2.DownloadListener? listener) => + _i5.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBackgroundColor(_i6.Color? color) => + _i5.Future setBackgroundColor(_i3.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future release() => + _i5.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_2()) as _i2.WebView); + returnValue: _FakeWebView_3()) as _i2.WebView); } /// A class which mocks [WebResourceRequest]. @@ -347,10 +354,10 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock } @override - _i4.Future Function(String, Map?) get loadUrl => + _i5.Future Function(String, Map?) get loadUrl => (super.noSuchMethod(Invocation.getter(#loadUrl), returnValue: (String url, Map? headers) => - Future.value()) as _i4.Future Function( + Future.value()) as _i5.Future Function( String, Map?)); @override void onDownloadStart(String? url, String? userAgent, @@ -362,7 +369,7 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock @override _i2.DownloadListener copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeDownloadListener_3()) as _i2.DownloadListener); + returnValue: _FakeDownloadListener_4()) as _i2.DownloadListener); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -375,10 +382,10 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock } @override - _i3.JavascriptChannelRegistry get javascriptChannelRegistry => + _i4.JavascriptChannelRegistry get javascriptChannelRegistry => (super.noSuchMethod(Invocation.getter(#javascriptChannelRegistry), - returnValue: _FakeJavascriptChannelRegistry_4()) - as _i3.JavascriptChannelRegistry); + returnValue: _FakeJavascriptChannelRegistry_5()) + as _i4.JavascriptChannelRegistry); @override String get channelName => (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '') @@ -390,7 +397,7 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock @override _i2.JavaScriptChannel copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeJavaScriptChannel_5()) as _i2.JavaScriptChannel); + returnValue: _FakeJavaScriptChannel_6()) as _i2.JavaScriptChannel); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -409,7 +416,7 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock @override _i2.WebChromeClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_6()) as _i2.WebChromeClient); + returnValue: _FakeWebChromeClient_7()) as _i2.WebChromeClient); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -430,13 +437,13 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock (super.noSuchMethod(Invocation.getter(#onPageFinishedCallback), returnValue: (String url) {}) as void Function(String)); @override - void Function(_i3.WebResourceError) get onWebResourceErrorCallback => + void Function(_i4.WebResourceError) get onWebResourceErrorCallback => (super.noSuchMethod(Invocation.getter(#onWebResourceErrorCallback), - returnValue: (_i3.WebResourceError error) {}) - as void Function(_i3.WebResourceError)); + returnValue: (_i4.WebResourceError error) {}) + as void Function(_i4.WebResourceError)); @override set onWebResourceErrorCallback( - void Function(_i3.WebResourceError)? _onWebResourceErrorCallback) => + void Function(_i4.WebResourceError)? _onWebResourceErrorCallback) => super.noSuchMethod( Invocation.setter( #onWebResourceErrorCallback, _onWebResourceErrorCallback), @@ -480,30 +487,30 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock returnValueForMissingStub: null); @override _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); + returnValue: _FakeWebViewClient_8()) as _i2.WebViewClient); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i3.JavascriptChannelRegistry { + implements _i4.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i3.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -513,17 +520,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i3.WebViewPlatformCallbacksHandler { + implements _i4.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i4.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i4.FutureOr); + returnValue: Future.value(false)) as _i5.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -537,7 +544,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i3.WebResourceError? error) => + void onWebResourceError(_i4.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -555,11 +562,11 @@ class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { (super.noSuchMethod( Invocation.method(#createWebView, [], {#useHybridComposition: useHybridComposition}), - returnValue: _FakeWebView_2()) as _i2.WebView); + returnValue: _FakeWebView_3()) as _i2.WebView); @override - _i4.Future setWebContentsDebuggingEnabled(bool? enabled) => + _i5.Future setWebContentsDebuggingEnabled(bool? enabled) => (super.noSuchMethod( Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } From c7a578125098ba0cce06b7c550cd84989fb79cec Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 13 Sep 2022 11:00:57 -0700 Subject: [PATCH 19/49] [camera] Add CameraInfo class and clean CameraX plugin (#6345) --- .../camera/camera_android_camerax/AUTHORS | 2 + .../camera_android_camerax/CHANGELOG.md | 1 + .../android/build.gradle | 29 ++- .../camerax/CameraAndroidCameraxPlugin.java | 76 +++++-- .../camerax/CameraInfoFlutterApiImpl.java | 24 ++ .../camerax/CameraInfoHostApiImpl.java | 23 ++ .../camerax/GeneratedCameraXLibrary.java | 199 +++++++++++++++++ .../plugins/camerax/InstanceManager.java | 205 ++++++++++++++++++ .../camerax/JavaObjectHostApiImpl.java | 33 +++ .../plugins/camerax/CameraInfoTest.java | 66 ++++++ .../plugins/camerax/CameraxPluginTest.java | 7 - .../plugins/camerax/InstanceManagerTest.java | 62 ++++++ .../camerax/JavaObjectHostApiTest.java | 32 +++ .../example/android/app/build.gradle | 4 +- .../example/android/build.gradle | 2 +- .../example/lib/main.dart | 22 +- .../example/pubspec.yaml | 65 +----- .../lib/camera_android_camerax.dart | 10 +- ...camera_android_camerax_method_channel.dart | 23 -- ...ra_android_camerax_platform_interface.dart | 36 --- .../lib/src/android_camera_camerax.dart | 21 ++ ...roid_camera_camerax_flutter_api_impls.dart | 44 ++++ .../lib/src/camera_info.dart | 91 ++++++++ .../lib/src/camerax_library.pigeon.dart | 160 ++++++++++++++ .../lib/src/instance_manager.dart | 199 +++++++++++++++++ .../lib/src/java_object.dart | 76 +++++++ .../pigeons/camerax_library.dart | 47 ++++ .../camera_android_camerax/pubspec.yaml | 59 +---- ...a_android_camerax_method_channel_test.dart | 29 --- .../test/camera_android_camerax_test.dart | 35 --- .../test/camera_info_test.dart | 60 +++++ .../test/camera_info_test.mocks.dart | 34 +++ .../test/instance_manager_test.dart | 174 +++++++++++++++ .../test/test_camerax_library.pigeon.dart | 79 +++++++ 34 files changed, 1744 insertions(+), 285 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java delete mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java delete mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart delete mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/camera_info.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/instance_manager.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/java_object.dart create mode 100644 packages/camera/camera_android_camerax/pigeons/camerax_library.dart delete mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart delete mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_info_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart create mode 100644 packages/camera/camera_android_camerax/test/instance_manager_test.dart create mode 100644 packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart diff --git a/packages/camera/camera_android_camerax/AUTHORS b/packages/camera/camera_android_camerax/AUTHORS index 513c0b0e2f86..557dff97933b 100644 --- a/packages/camera/camera_android_camerax/AUTHORS +++ b/packages/camera/camera_android_camerax/AUTHORS @@ -2,3 +2,5 @@ # to the Flutter project. Names should be added to the list like so: # # Name/Organization + +Google Inc. diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 044fecbb5272..0f305eb8b4fd 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,4 @@ ## NEXT * Creates camera_android_camerax plugin for development. +* Adds CameraInfo class and removes unnecessary code from plugin. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 4cb85eed3ba4..5e9a9a8ec106 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -22,7 +22,8 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + // CameraX dependencies require compilation against version 33 or later. + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -30,6 +31,30 @@ android { } defaultConfig { - minSdkVersion 16 + // Many of the CameraX APIs require API 21. + minSdkVersion 21 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +dependencies { + // CameraX core library using the camera2 implementation must use same version number. + def camerax_version = "1.2.0-beta01" + implementation "androidx.camera:camera-core:${camerax_version}" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'androidx.test:core:1.4.0' +} + testOptions { + unitTests.includeAndroidResources = true + unitTests.returnDefaultValues = true + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 0e60b13c2cfa..029a6dc4c8b7 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -4,39 +4,69 @@ package io.flutter.plugins.camerax; +import android.content.Context; 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; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; + +/** Platform implementation of the camera_plugin implemented with the CameraX library. */ +public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { + private InstanceManager instanceManager; + private FlutterPluginBinding pluginBinding; + + /** + * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. + * + *

See {@code io.flutter.plugins.camera.MainActivity} for an example. + */ + public CameraAndroidCameraxPlugin() {} + + void setUp(BinaryMessenger binaryMessenger, Context context) { + // Set up instance manager. + instanceManager = + InstanceManager.open( + identifier -> { + new GeneratedCameraXLibrary.JavaObjectFlutterApi(binaryMessenger) + .dispose(identifier, reply -> {}); + }); + + // Set up Host APIs. + GeneratedCameraXLibrary.CameraInfoHostApi.setup( + binaryMessenger, new CameraInfoHostApiImpl(instanceManager)); + GeneratedCameraXLibrary.JavaObjectHostApi.setup( + binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); + } @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = - new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); - channel.setMethodCallHandler(this); + pluginBinding = flutterPluginBinding; + (new CameraAndroidCameraxPlugin()) + .setUp( + flutterPluginBinding.getBinaryMessenger(), + flutterPluginBinding.getApplicationContext()); } @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(); + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + if (instanceManager != null) { + instanceManager.close(); } } + // Activity Lifecycle methods: + @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - } + public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {} + + @Override + public void onDetachedFromActivityForConfigChanges() {} + + @Override + public void onReattachedToActivityForConfigChanges( + @NonNull ActivityPluginBinding activityPluginBinding) {} + + @Override + public void onDetachedFromActivity() {} } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java new file mode 100644 index 000000000000..b5ba9fc1ff3b --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import androidx.camera.core.CameraInfo; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoFlutterApi; + +public class CameraInfoFlutterApiImpl extends CameraInfoFlutterApi { + private final InstanceManager instanceManager; + + public CameraInfoFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + void create(CameraInfo cameraInfo, Reply reply) { + instanceManager.addHostCreatedInstance(cameraInfo); + create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java new file mode 100644 index 000000000000..7daba0d38d6a --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import androidx.annotation.NonNull; +import androidx.camera.core.CameraInfo; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoHostApi; + +public class CameraInfoHostApiImpl implements CameraInfoHostApi { + private final InstanceManager instanceManager; + + public CameraInfoHostApiImpl(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + @Override + public Long getSensorRotationDegrees(@NonNull Long identifier) { + CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(identifier); + return Long.valueOf(cameraInfo.getSensorRotationDegrees()); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java new file mode 100644 index 000000000000..ec3b81a8e9d8 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -0,0 +1,199 @@ +// 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. +// Autogenerated from Pigeon (v3.2.9), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package io.flutter.plugins.camerax; + +import android.util.Log; +import androidx.annotation.NonNull; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +public class GeneratedCameraXLibrary { + private static class JavaObjectHostApiCodec extends StandardMessageCodec { + public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); + + private JavaObjectHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface JavaObjectHostApi { + void dispose(@NonNull Long identifier); + + /** The codec used by JavaObjectHostApi. */ + static MessageCodec getCodec() { + return JavaObjectHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.dispose((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class JavaObjectFlutterApiCodec extends StandardMessageCodec { + public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec(); + + private JavaObjectFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class JavaObjectFlutterApi { + private final BinaryMessenger binaryMessenger; + + public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return JavaObjectFlutterApiCodec.INSTANCE; + } + + public void dispose(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + + private static class CameraInfoHostApiCodec extends StandardMessageCodec { + public static final CameraInfoHostApiCodec INSTANCE = new CameraInfoHostApiCodec(); + + private CameraInfoHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CameraInfoHostApi { + @NonNull + Long getSensorRotationDegrees(@NonNull Long identifier); + + /** The codec used by CameraInfoHostApi. */ + static MessageCodec getCodec() { + return CameraInfoHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `CameraInfoHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, CameraInfoHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Long output = + api.getSensorRotationDegrees( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class CameraInfoFlutterApiCodec extends StandardMessageCodec { + public static final CameraInfoFlutterApiCodec INSTANCE = new CameraInfoFlutterApiCodec(); + + private CameraInfoFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class CameraInfoFlutterApi { + private final BinaryMessenger binaryMessenger; + + public CameraInfoFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return CameraInfoFlutterApiCodec.INSTANCE; + } + + public void create(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraInfoFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + + private static Map wrapError(Throwable exception) { + Map errorMap = new HashMap<>(); + errorMap.put("message", exception.toString()); + errorMap.put("code", exception.getClass().getSimpleName()); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + return errorMap; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java new file mode 100644 index 000000000000..8981227073b5 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java @@ -0,0 +1,205 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.Nullable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.WeakHashMap; + +/** + * Maintains instances used to communicate with the corresponding objects in Dart. + * + *

When an instance is added with an identifier, either can be used to retrieve the other. + * + *

Added instances are added as a weak reference and a strong reference. When the strong + * reference is removed with `{@link #remove(long)}` and the weak reference is deallocated, the + * `finalizationListener` is made with the instance's identifier. However, if the strong reference + * is removed and then the identifier is retrieved with the intention to pass the identifier to Dart + * (e.g. calling {@link #getIdentifierForStrongReference(Object)}), the strong reference to the + * instance is recreated. The strong reference will then need to be removed manually again. + */ +@SuppressWarnings("unchecked") +public class InstanceManager { + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously from Dart. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + private static final long MIN_HOST_CREATED_IDENTIFIER = 65536; + private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000; + + /** Interface for listening when a weak reference of an instance is removed from the manager. */ + public interface FinalizationListener { + void onFinalize(long identifier); + } + + private final WeakHashMap identifiers = new WeakHashMap<>(); + private final HashMap> weakInstances = new HashMap<>(); + private final HashMap strongInstances = new HashMap<>(); + + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private final HashMap, Long> weakReferencesToIdentifiers = new HashMap<>(); + + private final Handler handler = new Handler(Looper.getMainLooper()); + + private final FinalizationListener finalizationListener; + + private long nextIdentifier = MIN_HOST_CREATED_IDENTIFIER; + private boolean isClosed = false; + + /** + * Instantiate a new manager. + * + *

When the manager is no longer needed, {@link #close()} must be called. + * + * @param finalizationListener the listener for garbage collected weak references. + * @return a new `InstanceManager`. + */ + public static InstanceManager open(FinalizationListener finalizationListener) { + return new InstanceManager(finalizationListener); + } + + private InstanceManager(FinalizationListener finalizationListener) { + this.finalizationListener = finalizationListener; + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); + } + + /** + * Removes `identifier` and its associated strongly referenced instance, if present, from the + * manager. + * + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the removed instance if the manager contains the given identifier, otherwise null. + */ + @Nullable + public T remove(long identifier) { + assertManagerIsNotClosed(); + return (T) strongInstances.remove(identifier); + } + + /** + * Retrieves the identifier paired with an instance. + * + *

If the manager contains `instance`, as a strong or weak reference, the strong reference to + * `instance` will be recreated and will need to be removed again with {@link #remove(long)}. + * + * @param instance an instance that may be stored in the manager. + * @return the identifier associated with `instance` if the manager contains the value, otherwise + * null. + */ + @Nullable + public Long getIdentifierForStrongReference(Object instance) { + assertManagerIsNotClosed(); + final Long identifier = identifiers.get(instance); + if (identifier != null) { + strongInstances.put(identifier, instance); + } + return identifier; + } + + /** + * Adds a new instance that was instantiated from Dart. + * + *

If an instance or identifier has already been added, it will be replaced by the new values. + * The Dart InstanceManager is considered the source of truth and has the capability to overwrite + * stored pairs in response to hot restarts. + * + * @param instance the instance to be stored. + * @param identifier the identifier to be paired with instance. This value must be >= 0. + */ + public void addDartCreatedInstance(Object instance, long identifier) { + assertManagerIsNotClosed(); + addInstance(instance, identifier); + } + + /** + * Adds a new instance that was instantiated from the host platform. + * + * @param instance the instance to be stored. + * @return the unique identifier stored with instance. + */ + public long addHostCreatedInstance(Object instance) { + assertManagerIsNotClosed(); + final long identifier = nextIdentifier++; + addInstance(instance, identifier); + return identifier; + } + + /** + * Retrieves the instance associated with identifier. + * + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the instance associated with `identifier` if the manager contains the value, otherwise + * null. + */ + @Nullable + public T getInstance(long identifier) { + assertManagerIsNotClosed(); + final WeakReference instance = (WeakReference) weakInstances.get(identifier); + if (instance != null) { + return instance.get(); + } + return (T) strongInstances.get(identifier); + } + + /** + * Returns whether this manager contains the given `instance`. + * + * @param instance the instance whose presence in this manager is to be tested. + * @return whether this manager contains the given `instance`. + */ + public boolean containsInstance(Object instance) { + assertManagerIsNotClosed(); + return identifiers.containsKey(instance); + } + + /** + * Closes the manager and releases resources. + * + *

Calling a method after calling this one will throw an {@link AssertionError}. This method + * excluded. + */ + public void close() { + handler.removeCallbacks(this::releaseAllFinalizedInstances); + isClosed = true; + } + + private void releaseAllFinalizedInstances() { + WeakReference reference; + while ((reference = (WeakReference) referenceQueue.poll()) != null) { + final Long identifier = weakReferencesToIdentifiers.remove(reference); + if (identifier != null) { + weakInstances.remove(identifier); + strongInstances.remove(identifier); + finalizationListener.onFinalize(identifier); + } + } + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); + } + + private void addInstance(Object instance, long identifier) { + if (identifier < 0) { + throw new IllegalArgumentException("Identifier must be >= 0."); + } + final WeakReference weakReference = new WeakReference<>(instance, referenceQueue); + identifiers.put(instance, identifier); + weakInstances.put(identifier, weakReference); + weakReferencesToIdentifiers.put(weakReference, identifier); + strongInstances.put(identifier, instance); + } + + private void assertManagerIsNotClosed() { + if (isClosed) { + throw new AssertionError("Manager has already been closed."); + } + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java new file mode 100644 index 000000000000..5dc0ba7fc8ba --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import androidx.annotation.NonNull; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.JavaObjectHostApi; + +/** + * A pigeon Host API implementation that handles creating {@link Object}s and invoking its static + * and instance methods. + * + *

{@link Object} instances created by {@link JavaObjectHostApiImpl} are used to intercommunicate + * with a paired Dart object. + */ +public class JavaObjectHostApiImpl implements JavaObjectHostApi { + private final InstanceManager instanceManager; + + /** + * Constructs a {@link JavaObjectHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with Dart objects + */ + public JavaObjectHostApiImpl(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + @Override + public void dispose(@NonNull Long identifier) { + instanceManager.remove(identifier); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java new file mode 100644 index 000000000000..663d0e2f26d6 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.camera.core.CameraInfo; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class CameraInfoTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public CameraInfo mockCameraInfo; + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void getSensorRotationDegreesTest() { + final CameraInfoHostApiImpl cameraInfoHostApi = new CameraInfoHostApiImpl(testInstanceManager); + + testInstanceManager.addDartCreatedInstance(mockCameraInfo, 1); + + when(mockCameraInfo.getSensorRotationDegrees()).thenReturn(90); + + assertEquals((long) cameraInfoHostApi.getSensorRotationDegrees(1L), 90L); + verify(mockCameraInfo).getSensorRotationDegrees(); + } + + @Test + public void flutterApiCreateTest() { + final CameraInfoFlutterApiImpl spyFlutterApi = + spy(new CameraInfoFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(mockCameraInfo, reply -> {}); + + final long identifier = + Objects.requireNonNull(testInstanceManager.getIdentifierForStrongReference(mockCameraInfo)); + verify(spyFlutterApi).create(eq(identifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java deleted file mode 100644 index 69b0e34f6bb6..000000000000 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.camerax; - -public class CameraxPluginTest {} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java new file mode 100644 index 000000000000..3878e05a40e8 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class InstanceManagerTest { + @Test + public void addDartCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.getInstance(0)); + assertEquals((Long) 0L, instanceManager.getIdentifierForStrongReference(object)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void addHostCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + long identifier = instanceManager.addHostCreatedInstance(object); + + assertNotNull(instanceManager.getInstance(identifier)); + assertEquals(object, instanceManager.getInstance(identifier)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void remove() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.remove(0)); + + // To allow for object to be garbage collected. + //noinspection UnusedAssignment + object = null; + + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java new file mode 100644 index 000000000000..cce3341aaa89 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +public class JavaObjectHostApiTest { + @Test + public void dispose() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final JavaObjectHostApiImpl hostApi = new JavaObjectHostApiImpl(instanceManager); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + // To free object for garbage collection. + //noinspection UnusedAssignment + object = null; + + hostApi.dispose(0L); + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} 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 1dbeb6653915..0c0cbcd06921 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { @@ -39,7 +39,7 @@ android { applicationId "io.flutter.plugins.cameraxexample" // 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 + minSdkVersion 21 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/camera/camera_android_camerax/example/android/build.gradle b/packages/camera/camera_android_camerax/example/android/build.gradle index 58a8c74b1474..20411f5f31a9 100644 --- a/packages/camera/camera_android_camerax/example/android/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:7.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index c5391096374e..244a15281e3f 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -2,17 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/material.dart'; -void main() { +late List _cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await CameraPlatform.instance.availableCameras(); + runApp(const MyApp()); } /// Example app class MyApp extends StatefulWidget { /// App instantiation - const MyApp({Key? key}) : super(key: key); - + const MyApp({super.key}); @override State createState() => _MyAppState(); } @@ -20,13 +25,18 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { + String availableCameraNames = 'Available cameras:'; + for (final CameraDescription cameraDescription in _cameras) { + availableCameraNames = '$availableCameraNames ${cameraDescription.name},'; + } return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('Camera Example'), ), - body: const Center( - child: Text('Hello, world!'), + body: Center( + child: Text(availableCameraNames.substring( + 0, availableCameraNames.length - 1)), ), ), ); diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 891e9486e846..2884e8685d8c 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -1,19 +1,11 @@ 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 +publish_to: 'none' environment: - sdk: '>=2.14.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' + flutter: ">=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: camera_android_camerax: # When depending on this package from a real application you should use: @@ -22,64 +14,15 @@ 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 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 - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - flutter_test: sdk: flutter - integration_test: sdk: flutter -# 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 + \ No newline at end of file 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 40f8c703fa9f..4ddecd71397b 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -2,12 +2,4 @@ // 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'; - -/// Android Camera implented with the CameraX library. -class CameraAndroidCamerax { - /// Returns platform version of this instance. - Future getPlatformVersion() { - return CameraAndroidCameraxPlatform.instance.getPlatformVersion(); - } -} +export 'src/android_camera_camerax.dart'; 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 deleted file mode 100644 index 699a3667d14d..000000000000 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart +++ /dev/null @@ -1,23 +0,0 @@ -// 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'; - -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 methodChannel = - const MethodChannel('camera_android_camerax'); - - @override - Future getPlatformVersion() async { - 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 deleted file mode 100644 index 03554caf3245..000000000000 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart +++ /dev/null @@ -1,36 +0,0 @@ -// 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'; - -/// The platform interface for the Android Camera implemented with CameraX. -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; - } - - /// Returns the platform version of this isntance. - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart new file mode 100644 index 000000000000..f03273861793 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -0,0 +1,21 @@ +// 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 'dart:async'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; + +/// The Android implementation of [CameraPlatform] that uses the CameraX library. +class AndroidCameraCameraX extends CameraPlatform { + /// Registers this class as the default instance of [CameraPlatform]. + static void registerWith() { + CameraPlatform.instance = AndroidCameraCameraX(); + } + + /// Returns list of all available cameras and their descriptions. + @override + Future> availableCameras() async { + throw UnimplementedError('availableCameras() is not implemented.'); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart new file mode 100644 index 000000000000..8190a1ce1161 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -0,0 +1,44 @@ +// 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_info.dart'; +import 'camerax_library.pigeon.dart'; +import 'java_object.dart'; + +/// Handles initialization of Flutter APIs for the Android CameraX library. +class AndroidCameraXCameraFlutterApis { + /// Creates a [AndroidCameraXCameraFlutterApis]. + AndroidCameraXCameraFlutterApis({ + JavaObjectFlutterApiImpl? javaObjectFlutterApi, + CameraInfoFlutterApiImpl? cameraInfoFlutterApi, + }) { + this.javaObjectFlutterApi = + javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); + this.cameraInfoFlutterApi = + cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); + } + + static bool _haveBeenSetUp = false; + + /// Mutable instance containing all Flutter Apis for Android CameraX Camera. + /// + /// This should only be changed for testing purposes. + static AndroidCameraXCameraFlutterApis instance = + AndroidCameraXCameraFlutterApis(); + + /// Handles callbacks methods for the native Java Object class. + late final JavaObjectFlutterApi javaObjectFlutterApi; + + /// Flutter Api for [CameraInfo]. + late final CameraInfoFlutterApiImpl cameraInfoFlutterApi; + + /// Ensures all the Flutter APIs have been setup to receive calls from native code. + void ensureSetUp() { + if (!_haveBeenSetUp) { + JavaObjectFlutterApi.setup(javaObjectFlutterApi); + CameraInfoFlutterApi.setup(cameraInfoFlutterApi); + _haveBeenSetUp = true; + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camera_info.dart b/packages/camera/camera_android_camerax/lib/src/camera_info.dart new file mode 100644 index 000000000000..3edc7b4afad2 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camera_info.dart @@ -0,0 +1,91 @@ +// 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' show BinaryMessenger; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Represents the metadata of a camera. +/// +/// See https://developer.android.com/reference/androidx/camera/core/CameraInfo. +class CameraInfo extends JavaObject { + /// Constructs a [CameraInfo] that is not automatically attached to a native object. + CameraInfo.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = CameraInfoHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final CameraInfoHostApiImpl _api; + + /// Gets sensor orientation degrees of camera. + Future getSensorRotationDegrees() => + _api.getSensorRotationDegreesFromInstance(this); +} + +/// Host API implementation of [CameraInfo]. +class CameraInfoHostApiImpl extends CameraInfoHostApi { + /// Constructs a [CameraInfoHostApiImpl]. + CameraInfoHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + /// Gets sensor orientation degrees of [CameraInfo]. + Future getSensorRotationDegreesFromInstance(CameraInfo instance) async { + final int sensorRotationDegrees = await getSensorRotationDegrees( + instanceManager.getIdentifier(instance)!); + return sensorRotationDegrees; + } +} + +/// Flutter API implementation of [CameraInfo]. +class CameraInfoFlutterApiImpl extends CameraInfoFlutterApi { + /// Constructs a [CameraInfoFlutterApiImpl]. + CameraInfoFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + CameraInfo.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager), + identifier, + onCopy: (CameraInfo original) { + return CameraInfo.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart new file mode 100644 index 000000000000..8d421836a587 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -0,0 +1,160 @@ +// 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. +// Autogenerated from Pigeon (v3.2.9), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class _JavaObjectHostApiCodec extends StandardMessageCodec { + const _JavaObjectHostApiCodec(); +} + +class JavaObjectHostApi { + /// Constructor for [JavaObjectHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + JavaObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _JavaObjectHostApiCodec(); + + Future dispose(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _JavaObjectFlutterApiCodec extends StandardMessageCodec { + const _JavaObjectFlutterApiCodec(); +} + +abstract class JavaObjectFlutterApi { + static const MessageCodec codec = _JavaObjectFlutterApiCodec(); + + void dispose(int identifier); + static void setup(JavaObjectFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return; + }); + } + } + } +} + +class _CameraInfoHostApiCodec extends StandardMessageCodec { + const _CameraInfoHostApiCodec(); +} + +class CameraInfoHostApi { + /// Constructor for [CameraInfoHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + CameraInfoHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _CameraInfoHostApiCodec(); + + Future getSensorRotationDegrees(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } +} + +class _CameraInfoFlutterApiCodec extends StandardMessageCodec { + const _CameraInfoFlutterApiCodec(); +} + +abstract class CameraInfoFlutterApi { + static const MessageCodec codec = _CameraInfoFlutterApiCodec(); + + void create(int identifier); + static void setup(CameraInfoFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart new file mode 100644 index 000000000000..dd48610c8b56 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart @@ -0,0 +1,199 @@ +// 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'; + +/// 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 { + /// Constructs an [InstanceManager]. + InstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + debugPrint('Releasing weak reference with identifier: $identifier'); + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } + + // 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 = {}; + final Map _copyCallbacks = {}; + 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( + T instance, { + required T Function(T original) onCopy, + }) { + assert(getIdentifier(instance) == null); + + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); + return identifier; + } + + /// 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. + /// + /// This does not remove the the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(Object instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { + return null; + } + + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; + } + + /// 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. + /// + /// This does not remove the the weak referenced instance associtated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + debugPrint('Releasing strong reference with identifier: $identifier'); + _copyCallbacks.remove(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 Object? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final Object? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final Object copy = + _copyCallbacks[identifier]!(strongInstance)! as Object; + _identifiers[copy] = identifier; + _weakInstances[identifier] = WeakReference(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; + } + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier(Object 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( + T instance, + int identifier, { + required T Function(T original) onCopy, + }) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); + } + + void _addInstanceWithIdentifier( + T instance, + int identifier, { + required T Function(T original) onCopy, + }) { + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final Object copy = onCopy(instance); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + _copyCallbacks[identifier] = onCopy; + } + + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); + } + + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/java_object.dart b/packages/camera/camera_android_camerax/lib/src/java_object.dart new file mode 100644 index 000000000000..36a29ed0517b --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/java_object.dart @@ -0,0 +1,76 @@ +// 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' show immutable; +import 'package:flutter/services.dart'; + +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; + +/// Root of the Java class hierarchy. +/// +/// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html. +@immutable +class JavaObject { + /// Constructs a [JavaObject] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + JavaObject.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _api = JavaObjectHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + /// Global instance of [InstanceManager]. + static final InstanceManager globalInstanceManager = InstanceManager( + onWeakReferenceRemoved: (int identifier) { + JavaObjectHostApiImpl().dispose(identifier); + }, + ); + + /// Pigeon Host Api implementation for [JavaObject]. + final JavaObjectHostApiImpl _api; + + /// Release the reference to a native Java instance. + static void dispose(JavaObject instance) { + instance._api.instanceManager.removeWeakReference(instance); + } +} + +/// Handles methods calls to the native Java Object class. +class JavaObjectHostApiImpl extends JavaObjectHostApi { + /// Constructs a [JavaObjectHostApiImpl]. + JavaObjectHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; +} + +/// Handles callbacks methods for the native Java Object class. +class JavaObjectFlutterApiImpl implements JavaObjectFlutterApi { + /// Constructs a [JavaObjectFlutterApiImpl]. + JavaObjectFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void dispose(int identifier) { + instanceManager.remove(identifier); + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart new file mode 100644 index 000000000000..d36634a9cac3 --- /dev/null +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -0,0 +1,47 @@ +// 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:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/camerax_library.pigeon.dart', + dartTestOut: 'test/test_camerax_library.pigeon.dart', + dartOptions: DartOptions(copyrightHeader: [ + '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.', + ]), + javaOut: + 'android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.camerax', + className: 'GeneratedCameraXLibrary', + copyrightHeader: [ + '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.', + ], + ), + ), +) +@HostApi(dartHostTestHandler: 'TestJavaObjectHostApi') +abstract class JavaObjectHostApi { + void dispose(int identifier); +} + +@FlutterApi() +abstract class JavaObjectFlutterApi { + void dispose(int identifier); +} + +@HostApi(dartHostTestHandler: 'TestCameraInfoHostApi') +abstract class CameraInfoHostApi { + int getSensorRotationDegrees(int identifier); +} + +@FlutterApi() +abstract class CameraInfoFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index bb5b15b6baf4..9873db1a0121 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -1,73 +1,30 @@ name: camera_android_camerax -description: A new Flutter plugin project. +description: Android implementation of the camera plugin using the CameraX library. 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 -homepage: publish_to: 'none' environment: - sdk: '>=2.14.0 <3.0.0' - flutter: ">=2.5.0" + sdk: '>=2.17.0 <3.0.0' + flutter: ">=3.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: implements: camera platforms: android: package: io.flutter.plugins.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 + dartPluginClass: AndroidCameraCameraX dependencies: + camera_platform_interface: ^2.2.0 flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 dev_dependencies: - flutter_lints: ^2.0.0 + build_runner: ^2.1.4 flutter_test: sdk: flutter + mockito: ^5.1.0 + pigeon: ^3.2.6 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 deleted file mode 100644 index 0a1ea847e1a5..000000000000 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -// 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:camera_android_camerax/camera_android_camerax_method_channel.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - final 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 deleted file mode 100644 index 05ff46c1c55d..000000000000 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -// 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:camera_android_camerax/camera_android_camerax.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'); -} - -void main() { - final CameraAndroidCameraxPlatform initialPlatform = - CameraAndroidCameraxPlatform.instance; - - test('$MethodChannelCameraAndroidCamerax is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - final CameraAndroidCamerax cameraAndroidCameraxPlugin = - CameraAndroidCamerax(); - final MockCameraAndroidCameraxPlatform fakePlatform = - MockCameraAndroidCameraxPlatform(); - CameraAndroidCameraxPlatform.instance = fakePlatform; - - expect(await cameraAndroidCameraxPlugin.getPlatformVersion(), '42'); - }); -} diff --git a/packages/camera/camera_android_camerax/test/camera_info_test.dart b/packages/camera/camera_android_camerax/test/camera_info_test.dart new file mode 100644 index 000000000000..eda822b33f73 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_info_test.dart @@ -0,0 +1,60 @@ +// 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:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'camera_info_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestCameraInfoHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('CameraInfo', () { + tearDown(() => TestCameraInfoHostApi.setup(null)); + + test('getSensorRotationDegreesTest', () async { + final MockTestCameraInfoHostApi mockApi = MockTestCameraInfoHostApi(); + TestCameraInfoHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraInfo cameraInfo = CameraInfo.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance( + cameraInfo, + 0, + onCopy: (_) => CameraInfo.detached(), + ); + + when(mockApi.getSensorRotationDegrees( + instanceManager.getIdentifier(cameraInfo))) + .thenReturn(90); + expect(await cameraInfo.getSensorRotationDegrees(), equals(90)); + + verify(mockApi.getSensorRotationDegrees(0)); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraInfoFlutterApi flutterApi = CameraInfoFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0); + + expect( + instanceManager.getInstanceWithWeakReference(0), isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart new file mode 100644 index 000000000000..e1f1e3ca9e9b --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart @@ -0,0 +1,34 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in camera_android_camerax/test/camera_info_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [TestCameraInfoHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestCameraInfoHostApi extends _i1.Mock + implements _i2.TestCameraInfoHostApi { + MockTestCameraInfoHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + int getSensorRotationDegrees(int? identifier) => (super.noSuchMethod( + Invocation.method(#getSensorRotationDegrees, [identifier]), + returnValue: 0) as int); +} diff --git a/packages/camera/camera_android_camerax/test/instance_manager_test.dart b/packages/camera/camera_android_camerax/test/instance_manager_test.dart new file mode 100644 index 000000000000..9562c41674ae --- /dev/null +++ b/packages/camera/camera_android_camerax/test/instance_manager_test.dart @@ -0,0 +1,174 @@ +// 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:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('InstanceManager', () { + test('addHostCreatedInstance', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect( + () => instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance( + Object(), + 0, + onCopy: (_) => Object(), + ), + throwsAssertionError, + ); + }); + + test('addDartCreatedInstance', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addDartCreatedInstance( + object, + onCopy: (_) => Object(), + ); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + final Object object = Object(); + + int? weakInstanceId; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); + }); + + test('removeWeakReference removes only weak reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.removeWeakReference(object), 0); + final Object copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); + }); + + test('removeStrongReference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); + }); + + test('removeStrongReference removes only strong reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('getInstance can add a new weak reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + instanceManager.removeWeakReference(object); + + final Object newWeakCopy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart new file mode 100644 index 000000000000..59e76190188e --- /dev/null +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -0,0 +1,79 @@ +// 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. +// Autogenerated from Pigeon (v3.2.9), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; + +class _TestJavaObjectHostApiCodec extends StandardMessageCodec { + const _TestJavaObjectHostApiCodec(); +} + +abstract class TestJavaObjectHostApi { + static const MessageCodec codec = _TestJavaObjectHostApiCodec(); + + void dispose(int identifier); + static void setup(TestJavaObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return {}; + }); + } + } + } +} + +class _TestCameraInfoHostApiCodec extends StandardMessageCodec { + const _TestCameraInfoHostApiCodec(); +} + +abstract class TestCameraInfoHostApi { + static const MessageCodec codec = _TestCameraInfoHostApiCodec(); + + int getSensorRotationDegrees(int identifier); + static void setup(TestCameraInfoHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees was null, expected non-null int.'); + final int output = api.getSensorRotationDegrees(arg_identifier!); + return {'result': output}; + }); + } + } + } +} From 822f6682c32722c16e4ef0a0fb6a10d76b41d0ac Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 13 Sep 2022 11:01:00 -0700 Subject: [PATCH 20/49] Manual update of scorecards to 2.0.3 (#6416) --- .github/workflows/scorecards-analysis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 5f51be13913c..3bcfdad7fb6b 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -17,6 +17,8 @@ jobs: security-events: write actions: read contents: read + # Needed to access OIDC token. + id-token: write steps: - name: "Checkout code" @@ -25,7 +27,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@ce330fde6b1a5c9c75b417e7efc510b822a35564 + uses: ossf/scorecard-action@865b4092859256271290c77adbd10a43f4779972 with: results_file: results.sarif results_format: sarif From 17aed719aaed423d70e29cd79a594e3369182edf Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 13 Sep 2022 11:02:36 -0700 Subject: [PATCH 21/49] [camera] Move ignores for body_might_complete_normally_catch_error (#6339) --- .../camera/camera_android/lib/src/android_camera.dart | 11 +++++------ .../lib/src/avfoundation_camera.dart | 11 +++++------ .../lib/src/method_channel/method_channel_camera.dart | 11 +++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index b02929ec8c8c..3e9e52af04a7 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -138,12 +138,11 @@ class AndroidCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index d4f986074671..19054fe5c561 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -138,12 +138,11 @@ class AVFoundationCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index fd9ad530bd27..eb6e59d9b4a1 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -130,12 +130,11 @@ class MethodChannelCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; From 2802ca46904b11236f9a93f2a12982541690597d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Sep 2022 14:56:09 -0400 Subject: [PATCH 22/49] Roll Flutter from 4930444f4afe to 2d5e181b7308 (32 revisions) (#6422) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 74c1c9a9cd2b..aa2123aef757 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4930444f4afe85272bbdc84b43196d59dad71166 +2d5e181b7308f5b639123b231cbf65ca94690b3d From 5e7c779e78c16a4ed00289ac9c3e3b5ad4c37921 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Tue, 13 Sep 2022 13:28:29 -0700 Subject: [PATCH 23/49] [in_app_purchase] Add immediateAndChargeFullPrice ProrationMode (#6415) --- .../in_app_purchase_android/CHANGELOG.md | 3 +- .../billing_client_wrapper.dart | 15 +++++-- .../billing_client_wrapper.g.dart | 1 + .../in_app_purchase_android/pubspec.yaml | 2 +- .../billing_client_wrapper_test.dart | 39 +++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 910c5cdc1fc0..8733a1c4133c 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.3+4 * Updates minimum Flutter version to 2.10. +* Adds IMMEDIATE_AND_CHARGE_FULL_PRICE to the `ProrationMode`. ## 0.2.3+3 diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 4c64a99e7a22..b64eaab49a9d 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -495,7 +495,8 @@ enum ProrationMode { @JsonValue(0) unknownSubscriptionUpgradeDowngradePolicy, - /// Replacement takes effect immediately, and the remaining time will be prorated and credited to the user. + /// Replacement takes effect immediately, and the remaining time will be prorated + /// and credited to the user. /// /// This is the current default behavior. @JsonValue(1) @@ -508,15 +509,23 @@ enum ProrationMode { @JsonValue(2) immediateAndChargeProratedPrice, - /// Replacement takes effect immediately, and the new price will be charged on next recurrence time. + /// Replacement takes effect immediately, and the new price will be charged on next + /// recurrence time. /// /// The billing cycle stays the same. @JsonValue(3) immediateWithoutProration, - /// Replacement takes effect when the old plan expires, and the new price will be charged at the same time. + /// Replacement takes effect when the old plan expires, and the new price will + /// be charged at the same time. @JsonValue(4) deferred, + + /// Replacement takes effect immediately, and the user is charged full price + /// of new plan and is given a full billing cycle of subscription, plus + /// remaining prorated time from the old plan. + @JsonValue(5) + immediateAndChargeFullPrice, } /// Serializer for [ProrationMode]. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart index efe7656d2138..99355a1b91fb 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart @@ -32,6 +32,7 @@ const _$ProrationModeEnumMap = { ProrationMode.immediateAndChargeProratedPrice: 2, ProrationMode.immediateWithoutProration: 3, ProrationMode.deferred: 4, + ProrationMode.immediateAndChargeFullPrice: 5, }; const _$BillingClientFeatureEnumMap = { diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 320e4b818e83..a0563f1434b8 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.3+3 +version: 0.2.3+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart index e803e81f7803..4dae957e21eb 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart @@ -311,6 +311,45 @@ void main() { const ProrationModeConverter().toJson(prorationMode)); }); + test( + 'serializes and deserializes data when using immediateAndChargeFullPrice', + () async { + const String debugMessage = 'dummy message'; + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( + responseCode: responseCode, debugMessage: debugMessage); + stubPlatform.addResponse( + name: launchMethodName, + value: buildBillingResultMap(expectedBillingResult), + ); + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; + const ProrationMode prorationMode = + ProrationMode.immediateAndChargeFullPrice; + + expect( + await billingClient.launchBillingFlow( + sku: skuDetails.sku, + accountId: accountId, + obfuscatedProfileId: profileId, + oldSku: dummyOldPurchase.sku, + prorationMode: prorationMode, + purchaseToken: dummyOldPurchase.purchaseToken), + equals(expectedBillingResult)); + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; + expect(arguments['sku'], equals(skuDetails.sku)); + expect(arguments['accountId'], equals(accountId)); + expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); + expect(arguments['obfuscatedProfileId'], equals(profileId)); + expect( + arguments['purchaseToken'], equals(dummyOldPurchase.purchaseToken)); + expect(arguments['prorationMode'], + const ProrationModeConverter().toJson(prorationMode)); + }); + test('handles null accountId', () async { const String debugMessage = 'dummy message'; const BillingResponse responseCode = BillingResponse.ok; From b512d780b4e23de817ba37afe1842d03612764bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 14 Sep 2022 11:51:01 -0400 Subject: [PATCH 24/49] Roll Flutter from 2d5e181b7308 to 4e8a28d29db7 (26 revisions) (#6427) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index aa2123aef757..20cb21b1533d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2d5e181b7308f5b639123b231cbf65ca94690b3d +4e8a28d29db7c26988c58ecaa692cb79f5e71394 From 993170f9d9167b3cef7da64da4e458bfcd60a3a8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 14 Sep 2022 11:53:06 -0400 Subject: [PATCH 25/49] Roll Flutter (stable) from 4f9d92fbbdf0 to e3c29ec00c9c (3 revisions) (#6426) --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index d9b146dd7f87..4c15f9c261c3 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -4f9d92fbbdf072a70a70d2179a9f87392b94104c +e3c29ec00c9c825c891d75054c63fcc46454dca1 From 64a2931d7f8e093c2adb8ea78187749ce1cfd259 Mon Sep 17 00:00:00 2001 From: Milvintsiss <38794405+Milvintsiss@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:19:19 +0200 Subject: [PATCH 26/49] [google_sign_in_android] Corrects typo in logs (#6216) --- .../google_sign_in_android/CHANGELOG.md | 3 ++- .../plugins/googlesignin/GoogleSignInPlugin.java | 16 ++++++---------- .../google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index e3e404fb48b3..b10c578cd125 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.1.1 +* Corrects typos in plugin error logs and removes not actionable warnings. * Updates minimum Flutter version to 2.10. ## 6.1.0 diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index e84d196a6a0d..d345d4976c63 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -356,16 +356,12 @@ public void init( // Android apps are identified by their package name and the SHA-1 of their signing key. // https://developers.google.com/android/guides/client-auth // https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project - if (!Strings.isNullOrEmpty(clientId)) { - if (Strings.isNullOrEmpty(serverClientId)) { - Log.w( - "google_sing_in", - "clientId is not supported on Android and is interpreted as serverClientId." - + "Use serverClientId instead to suppress this warning."); - serverClientId = clientId; - } else { - Log.w("google_sing_in", "clientId is not supported on Android and is ignored."); - } + if (!Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(serverClientId)) { + Log.w( + "google_sign_in", + "clientId is not supported on Android and is interpreted as serverClientId. " + + "Use serverClientId instead to suppress this warning."); + serverClientId = clientId; } if (Strings.isNullOrEmpty(serverClientId)) { diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index 941ca2c8d774..086a15117c2e 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.1.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" From dc3bfc6d371e842e860c4d5b9c847f6f119049d5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 15 Sep 2022 11:57:58 -0400 Subject: [PATCH 27/49] Roll Flutter from 4e8a28d29db7 to 9c0901ed63d0 (25 revisions) (#6431) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 20cb21b1533d..8fef684e8ba6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4e8a28d29db7c26988c58ecaa692cb79f5e71394 +9c0901ed63d036f73fdf52ca9cf485149b56f798 From feed66b874176c67ae55a6cc3e9203694bc9ef2d Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Thu, 15 Sep 2022 09:53:28 -0700 Subject: [PATCH 28/49] [plugin_platform_interface] Switch `PlatformInterface._instanceToken` to an expando. (#6411) This change replaces `PlatformInterface._instanceToken` (an instance field that points from a `PlatformInterface` to its corresponding token) with an expando that maps from `PlatformInterface` to `Object`. There is no change to the public API, and no change to the behavior of users' production code. This change ensures that if a customer tries to implement `PlatformInterface` using `implements` rather than `extends`, the code in `PlatformInterface._verify` won't try to access the private `_instanceToken` field in `PlatformInterface`. This is important because an upcoming change to the dart language is going to cause such accesses to throw exceptions rather than deferring to `noSuchMethod` (see https://github.com/dart-lang/language/issues/2020 for details). Fixes https://github.com/flutter/flutter/issues/109339. --- .../plugin_platform_interface/CHANGELOG.md | 6 +++++- .../lib/plugin_platform_interface.dart | 19 +++++++++++++++---- .../plugin_platform_interface/pubspec.yaml | 2 +- .../test/plugin_platform_interface_test.dart | 13 +++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 0e9b701444fd..0b5a6b63a52f 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,7 +1,11 @@ -## NEXT +## 2.1.3 * Minor fixes for new analysis options. * Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`. +* Modifies `PlatformInterface` to use an expando for detecting if a customer + tries to implement PlatformInterface using `implements` rather than `extends`. + This ensures that `verify` will continue to work as advertized after + https://github.com/dart-lang/language/issues/2020 is implemented. ## 2.1.2 diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart index a03c9ce2d367..6733b29953b0 100644 --- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart +++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart @@ -44,9 +44,20 @@ abstract class PlatformInterface { /// derived classes. /// /// @param token The same, non-`const` `Object` that will be passed to `verify`. - PlatformInterface({required Object token}) : _instanceToken = token; + PlatformInterface({required Object token}) { + _instanceTokens[this] = token; + } - final Object? _instanceToken; + /// Expando mapping instances of PlatformInterface to their associated tokens. + /// The reason this is not simply a private field of type `Object?` is because + /// as of the implementation of field promotion in Dart + /// (https://github.com/dart-lang/language/issues/2020), it is a runtime error + /// to invoke a private member that is mocked in another library. The expando + /// approach prevents [_verify] from triggering this runtime exception when + /// encountering an implementation that uses `implements` rather than + /// `extends`. This in turn allows [_verify] to throw an [AssertionError] (as + /// documented). + static final Expando _instanceTokens = Expando(); /// Ensures that the platform instance was constructed with a non-`const` token /// that matches the provided token and throws [AssertionError] if not. @@ -89,10 +100,10 @@ abstract class PlatformInterface { return; } if (preventConstObject && - identical(instance._instanceToken, const Object())) { + identical(_instanceTokens[instance], const Object())) { throw AssertionError('`const Object()` cannot be used as the token.'); } - if (!identical(token, instance._instanceToken)) { + if (!identical(token, _instanceTokens[instance])) { throw AssertionError( 'Platform interfaces must not be implemented with `implements`'); } diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index f4800d444e66..6a4bc488693b 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version # that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on: # `plugin_platform_interface: >=2.X.Y <4.0.0`). -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 329cecb16091..869017cd4f23 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -21,6 +21,12 @@ class SamplePluginPlatform extends PlatformInterface { class ImplementsSamplePluginPlatform extends Mock implements SamplePluginPlatform {} +class ImplementsSamplePluginPlatformUsingNoSuchMethod + implements SamplePluginPlatform { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock with MockPlatformInterfaceMixin implements SamplePluginPlatform {} @@ -98,6 +104,13 @@ void main() { }, throwsA(isA())); }); + test('prevents implmentation with `implements` and `noSuchMethod`', () { + expect(() { + SamplePluginPlatform.instance = + ImplementsSamplePluginPlatformUsingNoSuchMethod(); + }, throwsA(isA())); + }); + test('allows mocking with `implements`', () { final SamplePluginPlatform mock = ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin(); From 5159d5ec00478950a8ce24945a73a9567e4b9aaa Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Thu, 15 Sep 2022 23:40:57 +0200 Subject: [PATCH 29/49] [in_app_purchase] Replace errorColor to land deprecations (Part 2) (#6417) --- packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 3 ++- .../in_app_purchase_storekit/example/lib/main.dart | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 1982a45e3e99..2952c4b31abf 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.3.2+1 * Updates minimum Flutter version to 2.10. +* Replaces deprecated ThemeData.primaryColor. ## 0.3.2 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 72086eeb65a5..aa03190b0454 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -336,7 +336,7 @@ class _MyAppState extends State<_MyApp> { children: [ TextButton( style: TextButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, + backgroundColor: Theme.of(context).colorScheme.primary, // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 // ignore: deprecated_member_use primary: Colors.white, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 69bc6c652c5a..076837198c76 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.2 +version: 0.3.2+1 environment: sdk: ">=2.14.0 <3.0.0" From a6bf3b6332d3d5b0232256c486a2eb0058568f5d Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 16 Sep 2022 07:48:17 -0700 Subject: [PATCH 30/49] [ci] Update Cirrus to macOS Ventura and Xcode 14 (#6436) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 7a8e31dae0fb..01de99c23e9c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -64,7 +64,7 @@ macos_intel_template: &MACOS_INTEL_TEMPLATE macos_arm_template: &MACOS_ARM_TEMPLATE << : *MACOS_TEMPLATE macos_instance: - image: ghcr.io/cirruslabs/macos-monterey-xcode:13.4 + image: ghcr.io/cirruslabs/macos-ventura-xcode:14 # Light-workload Linux tasks. # These use default machines, with fewer CPUs, to reduce pressure on the From 6b24ec1e91f8d28ccb584c0e7c1b82df53858d0e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 16 Sep 2022 15:11:21 -0400 Subject: [PATCH 31/49] Roll Flutter from 9c0901ed63d0 to 7714a8d034ad (33 revisions) (#6440) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8fef684e8ba6..1da87c32c07a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9c0901ed63d036f73fdf52ca9cf485149b56f798 +7714a8d034ad418bcfcf421e12f45f121c356948 From c240f5dea2b922535292deae915565130633b18a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 17 Sep 2022 11:45:23 -0400 Subject: [PATCH 32/49] Roll Flutter from 7714a8d034ad to 5816d20b86b9 (23 revisions) (#6443) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1da87c32c07a..c78f85061bcb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7714a8d034ad418bcfcf421e12f45f121c356948 +5816d20b86b95205c40921fa91ee3434b9c97ac6 From 7d4cdf2e8854307d03502e8309b34eccd2564e04 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 18 Sep 2022 12:35:24 -0400 Subject: [PATCH 33/49] Roll Flutter from 5816d20b86b9 to c07af53b3cb2 (5 revisions) (#6445) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c78f85061bcb..faf70b19aedf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5816d20b86b95205c40921fa91ee3434b9c97ac6 +c07af53b3cb2182d7735f022969efb00f2ddbd27 From 451f400f3c732896bd7b029d255e4056ecbeb72c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 19 Sep 2022 11:51:14 -0400 Subject: [PATCH 34/49] Roll Flutter from c07af53b3cb2 to 99475b1b0bee (10 revisions) (#6455) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index faf70b19aedf..a9baeda10e84 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c07af53b3cb2182d7735f022969efb00f2ddbd27 +99475b1b0bee6ab09d0282b255c6b8c7e01ca4fe From 2cbb314d99d800cb4e1229ce6b304755303bbd64 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Mon, 19 Sep 2022 21:05:16 +0200 Subject: [PATCH 35/49] [image_picker] Allow saving photos picked with PHPicker without permissions (#6429) --- .../image_picker_ios/CHANGELOG.md | 4 +++ .../PickerSaveImageToPathOperationTests.m | 26 +++++++++++++++---- .../image_picker_ios/example/lib/main.dart | 23 ++++++++++------ .../ios/Classes/FLTImagePickerPlugin.m | 17 ++++++------ .../FLTPHPickerSaveImageToPathOperation.h | 1 + .../FLTPHPickerSaveImageToPathOperation.m | 10 ++++++- .../image_picker_ios/pubspec.yaml | 2 +- 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 33e2b61b0e88..986f5c0ff6ca 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.6+1 + +* Fixes issue with crashing the app when picking images with PHPicker without providing `Photo Library Usage` permission. + ## 0.8.6 * Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m index 688f5fbee032..e04c4f2abb50 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -22,7 +22,7 @@ - (void)testSaveWebPImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSavePNGImage API_AVAILABLE(ios(14)) { @@ -32,7 +32,7 @@ - (void)testSavePNGImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { @@ -42,7 +42,7 @@ - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { @@ -52,7 +52,21 @@ - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; +} + +- (void)testSavePNGImageWithoutFullMetadata API_AVAILABLE(ios(14)) { + id photoAssetUtil = OCMClassMock([PHAsset class]); + + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"pngImage" + withExtension:@"png"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result fullMetadata:NO]; + OCMVerify(times(0), [photoAssetUtil fetchAssetsWithLocalIdentifiers:[OCMArg any] + options:[OCMArg any]]); } /** @@ -79,7 +93,8 @@ - (PHPickerResult *)createPickerResultWithProvider:(NSItemProvider *)itemProvide * * @param result the picker result */ -- (void)verifySavingImageWithPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { +- (void)verifySavingImageWithPickerResult:(PHPickerResult *)result + fullMetadata:(BOOL)fullMetadata API_AVAILABLE(ios(14)) { XCTestExpectation *pathExpectation = [self expectationWithDescription:@"Path was created"]; FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] @@ -87,6 +102,7 @@ - (void)verifySavingImageWithPickerResult:(PHPickerResult *)result API_AVAILABLE maxHeight:@100 maxWidth:@100 desiredImageQuality:@100 + fullMetadata:fullMetadata savedPathBlock:^(NSString *savedPath) { if ([[NSFileManager defaultManager] fileExistsAtPath:savedPath]) { [pathExpectation fulfill]; diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index c5372b8e7ad8..440f2f1d7cca 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -93,10 +93,15 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final List? pickedFileList = await _picker.getMultiImage( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: quality, + final List pickedFileList = + await _picker.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), + ), ); setState(() { _imageFileList = pickedFileList; @@ -111,11 +116,13 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final XFile? pickedFile = await _picker.getImage( + final XFile? pickedFile = await _picker.getImageFromSource( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: quality, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), ); setState(() { _setImageFileListFromFile(pickedFile); diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index fa1bb6650501..27b06ba994ef 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -490,14 +490,15 @@ - (void)picker:(PHPickerViewController *)picker for (int i = 0; i < results.count; i++) { PHPickerResult *result = results[i]; - FLTPHPickerSaveImageToPathOperation *operation = - [[FLTPHPickerSaveImageToPathOperation alloc] initWithResult:result - maxHeight:maxHeight - maxWidth:maxWidth - desiredImageQuality:desiredImageQuality - savedPathBlock:^(NSString *savedPath) { - pathList[i] = savedPath; - }]; + FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] + initWithResult:result + maxHeight:maxHeight + maxWidth:maxWidth + desiredImageQuality:desiredImageQuality + fullMetadata:self.callContext.requestFullMetadata + savedPathBlock:^(NSString *savedPath) { + pathList[i] = savedPath; + }]; [operationQueue addOperation:operation]; } [operationQueue waitUntilAllOperationsAreFinished]; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h index 7ba3d28ef3dd..8e0970725e90 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h @@ -26,6 +26,7 @@ maxHeight:(NSNumber *)maxHeight maxWidth:(NSNumber *)maxWidth desiredImageQuality:(NSNumber *)desiredImageQuality + fullMetadata:(BOOL)fullMetadata savedPathBlock:(void (^)(NSString *))savedPathBlock API_AVAILABLE(ios(14)); @end diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index a81c95f1b120..7c8fbc9ca7cf 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -13,6 +13,7 @@ @interface FLTPHPickerSaveImageToPathOperation () @property(assign, nonatomic) NSNumber *maxHeight; @property(assign, nonatomic) NSNumber *maxWidth; @property(assign, nonatomic) NSNumber *desiredImageQuality; +@property(assign, nonatomic) BOOL requestFullMetadata; @end @@ -28,6 +29,7 @@ - (instancetype)initWithResult:(PHPickerResult *)result maxHeight:(NSNumber *)maxHeight maxWidth:(NSNumber *)maxWidth desiredImageQuality:(NSNumber *)desiredImageQuality + fullMetadata:(BOOL)fullMetadata savedPathBlock:(GetSavedPath)savedPathBlock API_AVAILABLE(ios(14)) { if (self = [super init]) { if (result) { @@ -35,6 +37,7 @@ - (instancetype)initWithResult:(PHPickerResult *)result self.maxHeight = maxHeight; self.maxWidth = maxWidth; self.desiredImageQuality = desiredImageQuality; + self.requestFullMetadata = fullMetadata; getSavedPath = savedPathBlock; executing = NO; finished = NO; @@ -113,7 +116,12 @@ - (void)start { * Processes the image. */ - (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { - PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; + PHAsset *originalAsset; + // Only if requested, fetch the full "PHAsset" metadata, which requires "Photo Library Usage" + // permissions. + if (self.requestFullMetadata) { + originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; + } if (self.maxWidth != nil || self.maxHeight != nil) { localImage = [FLTImagePickerImageUtil scaledImage:localImage diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 5c30bf9d8c35..6c78b2340ed0 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.6 +version: 0.8.6+1 environment: sdk: ">=2.14.0 <3.0.0" From 8a96431792e80054ce8c236538baa54fa147406c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 20 Sep 2022 13:07:06 -0400 Subject: [PATCH 36/49] Roll Flutter from 99475b1b0bee to b55bbb7bffc7 (36 revisions) (#6456) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a9baeda10e84..0371be11bf87 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -99475b1b0bee6ab09d0282b255c6b8c7e01ca4fe +b55bbb7bffc79adf9cce33ebda0582df41aae280 From 7992b0f720750cd48e5a7fa9277973ca73046ff9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 21 Sep 2022 12:36:06 -0400 Subject: [PATCH 37/49] Roll Flutter from b55bbb7bffc7 to cf01ecd19e61 (29 revisions) (#6458) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0371be11bf87..427e69ccf7d7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b55bbb7bffc79adf9cce33ebda0582df41aae280 +cf01ecd19e61b436c6d7f9c0f6d57ef84aeb0257 From 7d5ff597e35b540f662c0134fa4a243c5055a1bb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 22 Sep 2022 12:11:56 -0400 Subject: [PATCH 38/49] [tools] Require implementation package README warning (#6459) --- .../camera_android_camerax/example/README.md | 10 +- packages/camera/camera_web/example/README.md | 14 +- .../camera/camera_windows/example/README.md | 10 +- .../file_selector_ios/example/README.md | 11 +- .../file_selector_linux/example/README.md | 11 +- .../file_selector_macos/example/README.md | 11 +- .../file_selector_web/example/README.md | 14 +- .../file_selector_windows/example/README.md | 11 +- .../example/README.md | 10 +- .../google_maps_flutter_ios/example/README.md | 10 +- .../google_maps_flutter_web/example/README.md | 15 +- .../google_sign_in_android/example/README.md | 10 +- .../google_sign_in_ios/example/README.md | 10 +- .../google_sign_in_web/example/README.md | 14 +- .../image_picker_android/example/README.md | 10 +- .../image_picker_for_web/example/README.md | 12 +- .../image_picker_ios/example/README.md | 10 +- .../image_picker_windows/example/README.md | 10 +- .../in_app_purchase_android/example/README.md | 63 +------- .../example/README.md | 80 +--------- .../local_auth_android/example/README.md | 10 +- .../local_auth_ios/example/README.md | 10 +- .../local_auth_windows/example/README.md | 10 +- .../path_provider_android/example/README.md | 10 +- .../path_provider_ios/example/README.md | 10 +- .../path_provider_linux/example/README.md | 10 +- .../path_provider_macos/example/README.md | 10 +- .../path_provider_windows/example/README.md | 10 +- .../quick_actions_android/example/README.md | 10 +- .../quick_actions_ios/example/README.md | 10 +- .../example/README.md | 10 +- .../shared_preferences_ios/example/README.md | 10 +- .../example/README.md | 10 +- .../example/README.md | 10 +- .../shared_preferences_web/example/README.md | 12 +- .../example/README.md | 10 +- .../url_launcher_android/example/README.md | 10 +- .../url_launcher_ios/example/README.md | 10 +- .../url_launcher_linux/example/README.md | 10 +- .../url_launcher_macos/example/README.md | 10 +- .../url_launcher_web/example/README.md | 15 +- .../url_launcher_windows/example/README.md | 10 +- .../video_player_android/example/README.md | 10 +- .../example/README.md | 10 +- .../video_player_web/example/README.md | 14 +- .../webview_flutter_android/example/README.md | 10 +- .../webview_flutter_web/example/README.md | 10 +- .../example/README.md | 10 +- script/tool/CHANGELOG.md | 6 + script/tool/lib/src/readme_check_command.dart | 66 +++++++- .../tool/test/readme_check_command_test.dart | 141 ++++++++++++++++++ 51 files changed, 615 insertions(+), 235 deletions(-) diff --git a/packages/camera/camera_android_camerax/example/README.md b/packages/camera/camera_android_camerax/example/README.md index 438e446d1921..96b8bb17dbff 100644 --- a/packages/camera/camera_android_camerax/example/README.md +++ b/packages/camera/camera_android_camerax/example/README.md @@ -1,3 +1,9 @@ -# camera_android_camerax_example +# Platform Implementation Test App -Demonstrates how to use the camera_android_camerax plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/camera/camera_web/example/README.md b/packages/camera/camera_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/camera/camera_web/example/README.md +++ b/packages/camera/camera_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/camera/camera_windows/example/README.md b/packages/camera/camera_windows/example/README.md index ee7326472eaf..96b8bb17dbff 100644 --- a/packages/camera/camera_windows/example/README.md +++ b/packages/camera/camera_windows/example/README.md @@ -1,3 +1,9 @@ -# camera_windows_example +# Platform Implementation Test App -Demonstrates how to use the camera_windows plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/file_selector/file_selector_ios/example/README.md b/packages/file_selector/file_selector_ios/example/README.md index 9ed63fdb669f..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_ios/example/README.md +++ b/packages/file_selector/file_selector_ios/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_ios` example +# Platform Implementation Test App -Demonstrates iOS implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). \ No newline at end of file +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/file_selector/file_selector_linux/example/README.md b/packages/file_selector/file_selector_linux/example/README.md index 2f9f8c0f824b..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_linux/example/README.md +++ b/packages/file_selector/file_selector_linux/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_linux` example +# Platform Implementation Test App -Demonstrates Linux implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/file_selector/file_selector_macos/example/README.md b/packages/file_selector/file_selector_macos/example/README.md index 782fe679fcb0..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_macos/example/README.md +++ b/packages/file_selector/file_selector_macos/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_macos` example +# Platform Implementation Test App -Demonstrates macOS implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/file_selector/file_selector_web/example/README.md b/packages/file_selector/file_selector_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/file_selector/file_selector_web/example/README.md +++ b/packages/file_selector/file_selector_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/file_selector/file_selector_windows/example/README.md b/packages/file_selector/file_selector_windows/example/README.md index c8a3cce44a9a..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_windows/example/README.md +++ b/packages/file_selector/file_selector_windows/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_windows` Example +# Platform Implementation Test App -Demonstrates Windows implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/README.md b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md index c8852649b065..96b8bb17dbff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md @@ -1,3 +1,9 @@ -# google_maps_flutter_example +# Platform Implementation Test App -Demonstrates how to use the google_maps_flutter plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md index c8852649b065..96b8bb17dbff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md @@ -1,3 +1,9 @@ -# google_maps_flutter_example +# Platform Implementation Test App -Demonstrates how to use the google_maps_flutter plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/README.md b/packages/google_maps_flutter/google_maps_flutter_web/example/README.md index 3cdecfab2ab9..0e51ae5ecbd2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -7,6 +17,3 @@ in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info. - -See [Plugin Tests > Web Tests > Mocks](https://github.com/flutter/flutter/wiki/Plugin-Tests#mocks) -in the Flutter wiki for more information about the `.mocks.dart` files in this package. \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md index 8eb153eb8efd..96b8bb17dbff 100644 --- a/packages/google_sign_in/google_sign_in_android/example/README.md +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -1,3 +1,9 @@ -# google_sign_in_android example +# Platform Implementation Test App -Exercises the Android implementation of `GoogleSignInPlatform`. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md index 04c3372dc3b0..96b8bb17dbff 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/README.md +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -1,3 +1,9 @@ -# google_sign_in_ios example +# Platform Implementation Test App -Exercises the iOS implementation of `GoogleSignInPlatform`. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/google_sign_in/google_sign_in_web/example/README.md b/packages/google_sign_in/google_sign_in_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/google_sign_in/google_sign_in_web/example/README.md +++ b/packages/google_sign_in/google_sign_in_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md index 16b5c51839f8..96b8bb17dbff 100755 --- a/packages/image_picker/image_picker_android/example/README.md +++ b/packages/image_picker/image_picker_android/example/README.md @@ -1,3 +1,9 @@ -# image_picker_example +# Platform Implementation Test App -Demonstrates how to use the `image_picker` plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/image_picker/image_picker_for_web/example/README.md b/packages/image_picker/image_picker_for_web/example/README.md index 4348451b14e2..0e51ae5ecbd2 100644 --- a/packages/image_picker/image_picker_for_web/example/README.md +++ b/packages/image_picker/image_picker_for_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md index 16b5c51839f8..96b8bb17dbff 100755 --- a/packages/image_picker/image_picker_ios/example/README.md +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -1,3 +1,9 @@ -# image_picker_example +# Platform Implementation Test App -Demonstrates how to use the `image_picker` plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md index 7f61053c6e30..96b8bb17dbff 100644 --- a/packages/image_picker/image_picker_windows/example/README.md +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -1,3 +1,9 @@ -# image_picker_windows_example +# Platform Implementation Test App -Demonstrates how to use the image_picker_windows plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/in_app_purchase/in_app_purchase_android/example/README.md b/packages/in_app_purchase/in_app_purchase_android/example/README.md index 255e838e5b93..96b8bb17dbff 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/README.md +++ b/packages/in_app_purchase/in_app_purchase_android/example/README.md @@ -1,58 +1,9 @@ -# In App Purchase Example +# Platform Implementation Test App -Demonstrates how to use the In App Purchase Android (IAP) Plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. -## Getting Started - -### Preparation - -There's a significant amount of setup required for testing in-app purchases -successfully, including registering new app IDs and store entries to use for -testing in the Play Developer Console. Google Play requires developers to -configure an app with in-app items for purchase to call their in-app-purchase -APIs. The Google Play Store has extensive documentation on how to do this, and -we've also included a high level guide below. - -* [Google Play Billing Overview](https://developer.android.com/google/play/billing/billing_overview) - -### Android - -1. Create a new app in the [Play Developer - Console](https://play.google.com/apps/publish/) (PDC). - -2. Sign up for a merchant's account in the PDC. - -3. Create IAPs in the PDC available for purchase in the app. The example assumes - the following SKU IDs exist: - - - `consumable`: A managed product. - - `upgrade`: A managed product. - - `subscription_silver`: A lower level subscription. - - `subscription_gold`: A higher level subscription. - - Make sure that all of the products are set to `ACTIVE`. - -4. Update `APP_ID` in `example/android/app/build.gradle` to match your package - ID in the PDC. - -5. Create an `example/android/keystore.properties` file with all your signing - information. `keystore.example.properties` exists as an example to follow. - It's impossible to use any of the `BillingClient` APIs from an unsigned APK. - See - [here](https://developer.android.com/studio/publish/app-signing#secure-shared-keystore) - and [here](https://developer.android.com/studio/publish/app-signing#sign-apk) - for more information. - -6. Build a signed apk. `flutter build apk` will work for this, the gradle files - in this project have been configured to sign even debug builds. - -7. Upload the signed APK from step 6 to the PDC, and publish that to the alpha - test channel. Add your test account as an approved tester. The - `BillingClient` APIs won't work unless the app has been fully published to - the alpha channel and is being used by an authorized test account. See - [here](https://support.google.com/googleplay/android-developer/answer/3131213) - for more info. - -8. Sign in to the test device with the test account from step #7. Then use - `flutter run` to install the app to the device and test like normal. - \ No newline at end of file +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/README.md b/packages/in_app_purchase/in_app_purchase_storekit/example/README.md index 9cf98bf02e79..96b8bb17dbff 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/README.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/README.md @@ -1,75 +1,9 @@ -# In App Purchase iOS Example +# Platform Implementation Test App -Demonstrates how to use the In App Purchase iOS (IAP) Plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. -## Getting Started - -### Preparation - -There's a significant amount of setup required for testing in app purchases -successfully, including registering new app IDs and store entries to use for -testing in App Store Connect. The App Store requires developers to configure -an app with in-app items for purchase to call their in-app-purchase APIs. -The App Store has extensive documentation on how to do this, and we've also -included a high level guide below. - -* [In-App Purchase (App Store)](https://developer.apple.com/in-app-purchase/) - -### iOS - -When using Xcode 12 and iOS 14 or higher you can run the example in the simulator or on a device without -having to configure an App in App Store Connect. The example app is set up to use StoreKit Testing configured -in the `example/ios/Runner/Configuration.storekit` file (as documented in the article [Setting Up StoreKit Testing in Xcode](https://developer.apple.com/documentation/xcode/setting_up_storekit_testing_in_xcode?language=objc)). -To run the application take the following steps (note that it will only work when running from Xcode): - -1. Open the example app with Xcode, `File > Open File` `example/ios/Runner.xcworkspace`; - -2. Within Xcode edit the current scheme, `Product > Scheme > Edit Scheme...` (or press `Command + Shift + ,`); - -3. Enable StoreKit testing: - a. Select the `Run` action; - b. Click `Options` in the action settings; - c. Select the `Configuration.storekit` for the StoreKit Configuration option. - -4. Click the `Close` button to close the scheme editor; - -5. Select the device you want to run the example App on; - -6. Run the application using `Product > Run` (or hit the run button). - -When testing on pre-iOS 14 you can't run the example app on a simulator and you will need to configure an app in App Store Connect. You can do so by following the steps below: - -1. Follow ["Workflow for configuring in-app - purchases"](https://help.apple.com/app-store-connect/#/devb57be10e7), a - detailed guide on all the steps needed to enable IAPs for an app. Complete - steps 1 ("Sign a Paid Applications Agreement") and 2 ("Configure in-app - purchases"). - - For step #2, "Configure in-app purchases in App Store Connect," you'll want - to create the following products: - - - A consumable with product ID `consumable` - - An upgrade with product ID `upgrade` - - An auto-renewing subscription with product ID `subscription_silver` - - An non-renewing subscription with product ID `subscription_gold` - -2. In XCode, `File > Open File` `example/ios/Runner.xcworkspace`. Update the - Bundle ID to match the Bundle ID of the app created in step #1. - -3. [Create a Sandbox tester - account](https://help.apple.com/app-store-connect/#/dev8b997bee1) to test the - in-app purchases with. - -4. Use `flutter run` to install the app and test it. Note that you need to test - it on a real device instead of a simulator. Next click on one of the products - in the example App, this enables the "SANDBOX ACCOUNT" section in the iOS - settings. You will now be asked to sign in with your sandbox test account to - complete the purchase (no worries you won't be charged). If for some reason - you aren't asked to sign-in or the wrong user is listed, go into the iOS - settings ("Settings" -> "App Store" -> "SANDBOX ACCOUNT") and update your - sandbox account from there. This procedure is explained in great detail in - the [Testing In-App Purchases with Sandbox](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox?language=objc) article. - - -**Important:** signing into any production service (including iTunes!) with the -sandbox test account will permanently invalidate it. +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md index bd004a77d86b..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_android/example/README.md +++ b/packages/local_auth/local_auth_android/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md index bd004a77d86b..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_ios/example/README.md +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/local_auth/local_auth_windows/example/README.md b/packages/local_auth/local_auth_windows/example/README.md index 8f48b8563cad..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_windows/example/README.md +++ b/packages/local_auth/local_auth_windows/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. \ No newline at end of file +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/path_provider/path_provider_android/example/README.md b/packages/path_provider/path_provider_android/example/README.md index 801f44409938..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_android/example/README.md +++ b/packages/path_provider/path_provider_android/example/README.md @@ -1,3 +1,9 @@ -# path_provider_example +# Platform Implementation Test App -Demonstrates how to use the path_provider plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/path_provider/path_provider_ios/example/README.md b/packages/path_provider/path_provider_ios/example/README.md index 801f44409938..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_ios/example/README.md +++ b/packages/path_provider/path_provider_ios/example/README.md @@ -1,3 +1,9 @@ -# path_provider_example +# Platform Implementation Test App -Demonstrates how to use the path_provider plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/path_provider/path_provider_linux/example/README.md b/packages/path_provider/path_provider_linux/example/README.md index 333d0f55cec7..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_linux/example/README.md +++ b/packages/path_provider/path_provider_linux/example/README.md @@ -1,3 +1,9 @@ -# path_provider_linux_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_linux plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/path_provider/path_provider_macos/example/README.md b/packages/path_provider/path_provider_macos/example/README.md index 158869595c01..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_macos/example/README.md +++ b/packages/path_provider/path_provider_macos/example/README.md @@ -1,3 +1,9 @@ -# path_provider_macos_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_macos plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/path_provider/path_provider_windows/example/README.md b/packages/path_provider/path_provider_windows/example/README.md index 63723991a2e9..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_windows/example/README.md +++ b/packages/path_provider/path_provider_windows/example/README.md @@ -1,3 +1,9 @@ -# path_provider_windows_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_windows plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/quick_actions/quick_actions_android/example/README.md b/packages/quick_actions/quick_actions_android/example/README.md index c8a629019fc9..96b8bb17dbff 100644 --- a/packages/quick_actions/quick_actions_android/example/README.md +++ b/packages/quick_actions/quick_actions_android/example/README.md @@ -1,3 +1,9 @@ -# quick_actions_example +# Platform Implementation Test App -Demonstrates how to use the quick_actions plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/quick_actions/quick_actions_ios/example/README.md b/packages/quick_actions/quick_actions_ios/example/README.md index c8a629019fc9..96b8bb17dbff 100644 --- a/packages/quick_actions/quick_actions_ios/example/README.md +++ b/packages/quick_actions/quick_actions_ios/example/README.md @@ -1,3 +1,9 @@ -# quick_actions_example +# Platform Implementation Test App -Demonstrates how to use the quick_actions plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/shared_preferences/shared_preferences_android/example/README.md b/packages/shared_preferences/shared_preferences_android/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_android/example/README.md +++ b/packages/shared_preferences/shared_preferences_android/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/shared_preferences/shared_preferences_ios/example/README.md b/packages/shared_preferences/shared_preferences_ios/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/README.md +++ b/packages/shared_preferences/shared_preferences_ios/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/shared_preferences/shared_preferences_linux/example/README.md b/packages/shared_preferences/shared_preferences_linux/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/README.md +++ b/packages/shared_preferences/shared_preferences_linux/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/shared_preferences/shared_preferences_macos/example/README.md b/packages/shared_preferences/shared_preferences_macos/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/README.md +++ b/packages/shared_preferences/shared_preferences_macos/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/shared_preferences/shared_preferences_web/example/README.md b/packages/shared_preferences/shared_preferences_web/example/README.md index 4348451b14e2..0e51ae5ecbd2 100644 --- a/packages/shared_preferences/shared_preferences_web/example/README.md +++ b/packages/shared_preferences/shared_preferences_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. diff --git a/packages/shared_preferences/shared_preferences_windows/example/README.md b/packages/shared_preferences/shared_preferences_windows/example/README.md index 30c7f7e50c3b..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/README.md +++ b/packages/shared_preferences/shared_preferences_windows/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_windows_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences_windows plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/url_launcher/url_launcher_android/example/README.md b/packages/url_launcher/url_launcher_android/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_android/example/README.md +++ b/packages/url_launcher/url_launcher_android/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/url_launcher/url_launcher_ios/example/README.md b/packages/url_launcher/url_launcher_ios/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_ios/example/README.md +++ b/packages/url_launcher/url_launcher_ios/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/url_launcher/url_launcher_linux/example/README.md b/packages/url_launcher/url_launcher_linux/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_linux/example/README.md +++ b/packages/url_launcher/url_launcher_linux/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/url_launcher/url_launcher_macos/example/README.md b/packages/url_launcher/url_launcher_macos/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_macos/example/README.md +++ b/packages/url_launcher/url_launcher_macos/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/url_launcher/url_launcher_web/example/README.md b/packages/url_launcher/url_launcher_web/example/README.md index 3cdecfab2ab9..0e51ae5ecbd2 100644 --- a/packages/url_launcher/url_launcher_web/example/README.md +++ b/packages/url_launcher/url_launcher_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -7,6 +17,3 @@ in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info. - -See [Plugin Tests > Web Tests > Mocks](https://github.com/flutter/flutter/wiki/Plugin-Tests#mocks) -in the Flutter wiki for more information about the `.mocks.dart` files in this package. \ No newline at end of file diff --git a/packages/url_launcher/url_launcher_windows/example/README.md b/packages/url_launcher/url_launcher_windows/example/README.md index e444852697b9..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_windows/example/README.md +++ b/packages/url_launcher/url_launcher_windows/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_windows_example +# Platform Implementation Test App -Demonstrates the url_launcher_windows plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/video_player/video_player_android/example/README.md b/packages/video_player/video_player_android/example/README.md index f5974e947c00..96b8bb17dbff 100644 --- a/packages/video_player/video_player_android/example/README.md +++ b/packages/video_player/video_player_android/example/README.md @@ -1,3 +1,9 @@ -# video_player_example +# Platform Implementation Test App -Demonstrates how to use the video_player plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/video_player/video_player_avfoundation/example/README.md b/packages/video_player/video_player_avfoundation/example/README.md index f5974e947c00..96b8bb17dbff 100644 --- a/packages/video_player/video_player_avfoundation/example/README.md +++ b/packages/video_player/video_player_avfoundation/example/README.md @@ -1,3 +1,9 @@ -# video_player_example +# Platform Implementation Test App -Demonstrates how to use the video_player plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/video_player/video_player_web/example/README.md b/packages/video_player/video_player_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/video_player/video_player_web/example/README.md +++ b/packages/video_player/video_player_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/webview_flutter/webview_flutter_android/example/README.md b/packages/webview_flutter/webview_flutter_android/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_android/example/README.md +++ b/packages/webview_flutter/webview_flutter_android/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/webview_flutter/webview_flutter_web/example/README.md b/packages/webview_flutter/webview_flutter_web/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_web/example/README.md +++ b/packages/webview_flutter/webview_flutter_web/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8dbb4840a672..f6dfb0e58266 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,9 @@ +## NEXT + +* Adds `readme-check` validation that the example/README.md for a federated + plugin's implementation packages has a warning about the intended use of the + example instead of the template boilerplate. + ## 0.10.0 * Improves the logic in `version-check` to determine what changes don't require diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 4acf997a0085..e3fbc7bc454d 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -104,11 +104,8 @@ class ReadmeCheckCommand extends PackageLoopingCommand { errors.add(blockValidationError); } - if (_containsTemplateBoilerplate(readmeLines)) { - printError('${indentation}The boilerplate section about getting started ' - 'with Flutter should not be left in.'); - errors.add('Contains template boilerplate'); - } + errors.addAll(_validateBoilerplate(readmeLines, + mainPackage: mainPackage, isExample: isExample)); // Check if this is the main readme for a plugin, and if so enforce extra // checks. @@ -284,10 +281,63 @@ ${indentation * 2}Please use standard capitalizations: ${sortedListString(expect return null; } - /// Returns true if the README still has the boilerplate from the - /// `flutter create` templates. - bool _containsTemplateBoilerplate(List readmeLines) { + /// Validates [readmeLines], outputing error messages for any issue and + /// returning an array of error summaries (if any). + /// + /// Returns an empty array if validation passes. + List _validateBoilerplate( + List readmeLines, { + required RepositoryPackage mainPackage, + required bool isExample, + }) { + final List errors = []; + + if (_containsTemplateFlutterBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate section about getting started ' + 'with Flutter should not be left in.'); + errors.add('Contains template boilerplate'); + } + + // Enforce a repository-standard message in implementation plugin examples, + // since they aren't typical examples, which has been a source of + // confusion for plugin clients who find them. + if (isExample && mainPackage.isPlatformImplementation) { + if (_containsExampleBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate should not be left in for a ' + "federated plugin implementation package's example."); + errors.add('Contains template boilerplate'); + } + if (!_containsImplementationExampleExplanation(readmeLines)) { + printError('${indentation}The example README for a platform ' + 'implementation package should warn readers about its intended ' + 'use. Please copy the example README from another implementation ' + 'package in this repository.'); + errors.add('Missing implementation package example warning'); + } + } + + return errors; + } + + /// Returns true if the README still has unwanted parts of the boilerplate + /// from the `flutter create` templates. + bool _containsTemplateFlutterBoilerplate(List readmeLines) { return readmeLines.any((String line) => line.contains('For help getting started with Flutter')); } + + /// Returns true if the README still has the generic description of an + /// example from the `flutter create` templates. + bool _containsExampleBoilerplate(List readmeLines) { + return readmeLines + .any((String line) => line.contains('Demonstrates how to use the')); + } + + /// Returns true if the README contains the repository-standard explanation of + /// the purpose of a federated plugin implementation's example. + bool _containsImplementationExampleExplanation(List readmeLines) { + return readmeLines.contains('# Platform Implementation Test App') && + readmeLines + .any((String line) => line.contains('This is a test app for')); + } } diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index 37224fddc56b..eb2b6c8e7512 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -176,6 +176,147 @@ samples, guidance on mobile development, and a full API reference. ); }); + test( + 'fails when a plugin implementation package example README has the ' + 'template boilerplate', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin_ios', packagesDir.childDirectory('a_plugin')); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_ios_example + +Demonstrates how to use the a_plugin_ios plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate should not be left in for a federated plugin ' + "implementation package's example."), + contains('Contains template boilerplate'), + ]), + ); + }); + + test( + 'allows the template boilerplate in the example README for packages ' + 'other than plugin implementation packages', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', + packagesDir.childDirectory('a_plugin'), + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + }, + ); + // Write a README with an OS support table so that the main README check + // passes. + package.readmeFile.writeAsStringSync(''' +# a_plugin + +| | Android | +|----------------|---------| +| **Support** | SDK 19+ | + +A great plugin. +'''); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_example + +Demonstrates how to use the a_plugin plugin. +'''); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + ]), + ); + }); + + test( + 'fails when a plugin implementation package example README does not have ' + 'the repo-standard message', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin_ios', packagesDir.childDirectory('a_plugin')); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_ios_example + +Some random description. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The example README for a platform implementation package ' + 'should warn readers about its intended use. Please copy the ' + 'example README from another implementation package in this ' + 'repository.'), + contains('Missing implementation package example warning'), + ]), + ); + }); + + test('passes for a plugin implementation package with the expected content', + () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', + packagesDir.childDirectory('a_plugin'), + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + }, + ); + // Write a README with an OS support table so that the main README check + // passes. + package.readmeFile.writeAsStringSync(''' +# a_plugin + +| | Android | +|----------------|---------| +| **Support** | SDK 19+ | + +A great plugin. +'''); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. +'''); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + ]), + ); + }); + test( 'fails when multi-example top-level example directory README still has ' 'application template boilerplate', () async { From 7c6c784c2bf51e99af25ad7a7c3bc2f7370e964d Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 23 Sep 2022 04:09:33 +0800 Subject: [PATCH 39/49] [video_player] Fix invalid link in CHANGELOG on pub.dev (#6446) --- packages/video_player/video_player/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7a47cce54a6e..53cfabf31e78 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -18,7 +18,7 @@ ## 2.4.3 -* Fixes Android to correctly display videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). +* Fixes Android to correctly display videos recorded in landscapeRight ([#60327](https://github.com/flutter/flutter/issues/60327)). * Fixes order-dependent unit tests. ## 2.4.2 From 0177355a8dd9962ca65ada80ad995557784bf285 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:50:45 -0400 Subject: [PATCH 40/49] [webview_flutter_android] Expose the Java InstanceManager (#6421) --- .../webview_flutter_android/CHANGELOG.md | 4 ++ .../webviewflutter/InstanceManager.java | 3 ++ .../webviewflutter/WebViewFlutterPlugin.java | 7 +++ .../webviewflutter/WebViewHostApiImpl.java | 5 +++ .../WebViewFlutterPluginTest.java | 45 +++++++++++++++++++ .../webview_flutter_android/pubspec.yaml | 2 +- 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 910d20dda6d1..b37ada2225f0 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.10.2 + +* Adds a getter to expose the Java InstanceManager. + ## 2.10.1 * Adds a method to the `WebView` wrapper to retrieve the X and Y positions simultaneously. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java index 306dc20be47d..fefd577ee9b5 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java @@ -15,6 +15,9 @@ /** * Maintains instances used to communicate with the corresponding objects in Dart. * + *

Objects stored in this container are represented by an object in Dart that is also stored in + * an InstanceManager with the same identifier. + * *

When an instance is added with an identifier, either can be used to retrieve the other. * *

Added instances are added as a weak reference and a strong reference. When the strong diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 8db976a26937..fe7615c664a4 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -8,6 +8,7 @@ import android.os.Handler; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -166,4 +167,10 @@ private void updateContext(Context context) { webViewHostApi.setContext(context); javaScriptChannelHostApi.setPlatformThreadHandler(new Handler(context.getMainLooper())); } + + /** Maintains instances used to communicate with the corresponding objects in Dart. */ + @Nullable + public InstanceManager getInstanceManager() { + return instanceManager; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index f257ace71b0d..778ad611d05f 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -511,4 +511,9 @@ public void setBackgroundColor(Long instanceId, Long color) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); webView.setBackgroundColor(color.intValue()); } + + /** Maintains instances used to communicate with the corresponding WebView Dart object. */ + public InstanceManager getInstanceManager() { + return instanceManager; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java new file mode 100644 index 000000000000..16dc6cf5de2b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java @@ -0,0 +1,45 @@ +// 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.webviewflutter; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import android.content.Context; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.platform.PlatformViewRegistry; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class WebViewFlutterPluginTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock Context mockContext; + + @Mock BinaryMessenger mockBinaryMessenger; + + @Mock PlatformViewRegistry mockViewRegistry; + + @Mock FlutterPlugin.FlutterPluginBinding mockPluginBinding; + + @Test + public void getInstanceManagerAfterOnAttachedToEngine() { + final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin(); + + when(mockPluginBinding.getApplicationContext()).thenReturn(mockContext); + when(mockPluginBinding.getPlatformViewRegistry()).thenReturn(mockViewRegistry); + when(mockPluginBinding.getBinaryMessenger()).thenReturn(mockBinaryMessenger); + + webViewFlutterPlugin.onAttachedToEngine(mockPluginBinding); + + assertNotNull(webViewFlutterPlugin.getInstanceManager()); + + webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 62f4cc97aa06..f5f2e3c34e69 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.10.1 +version: 2.10.2 environment: sdk: ">=2.14.0 <3.0.0" From ff5b28a8af065094ba6256df3fcd6325357c8883 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 15:52:25 -0700 Subject: [PATCH 41/49] [quick_actions]Migrate the plugin class to Swift, and remove custom modulemap (#6369) --- .../quick_actions_ios/CHANGELOG.md | 5 + .../RunnerTests/FLTQuickActionsPluginTests.m | 80 ++++++++------- .../FLTShortcutStateManagerTests.m | 1 - .../ios/Classes/FLTQuickActionsPlugin.h | 14 --- .../ios/Classes/FLTQuickActionsPlugin.m | 99 ------------------- .../FLTShortcutStateManager.h | 0 .../FLTQuickActionsPlugin_Test.h | 23 ----- .../ios/Classes/QuickActionsPlugin.modulemap | 11 --- .../ios/Classes/QuickActionsPlugin.swift | 90 +++++++++++++++++ .../ios/Classes/quick_actions_ios-umbrella.h | 10 -- .../ios/quick_actions_ios.podspec | 11 ++- .../quick_actions_ios/pubspec.yaml | 4 +- 12 files changed, 146 insertions(+), 202 deletions(-) delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m rename packages/quick_actions/quick_actions_ios/ios/Classes/{PrivateHeaders => }/FLTShortcutStateManager.h (100%) delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 7334bbd6a632..e002bd70ac09 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.1 + +* Removes custom modulemap file with "Test" submodule and private headers for Swift migration. +* Migrates `FLTQuickActionsPlugin` class to Swift. + ## 1.0.0 * Updates version to 1.0 to reflect current status. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m index b47f89848bbc..89651b573822 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m @@ -4,7 +4,6 @@ @import Flutter; @import quick_actions_ios; -@import quick_actions_ios.Test; @import XCTest; #import @@ -26,9 +25,9 @@ - (void)testHandleMethodCall_setShortcutItems { FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -45,9 +44,9 @@ - (void)testHandleMethodCall_clearShortcutItems { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"clearShortcutItems" arguments:nil]; FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -63,9 +62,9 @@ - (void)testHandleMethodCall_getLaunchAction { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getLaunchAction" arguments:nil]; - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -79,9 +78,9 @@ - (void)testHandleMethodCall_getLaunchAction { - (void)testHandleMethodCall_nonExistMethods { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"nonExist" arguments:nil]; - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result must be called."]; [plugin @@ -97,9 +96,9 @@ - (void)testHandleMethodCall_nonExistMethods { - (void)testApplicationPerformActionForShortcutItem { id mockChannel = OCMClassMock([FlutterMethodChannel class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -118,9 +117,9 @@ - (void)testApplicationPerformActionForShortcutItem { - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -138,9 +137,9 @@ - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { } - (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; BOOL launchResult = [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; XCTAssertTrue(launchResult, @@ -150,11 +149,14 @@ - (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { - (void)testApplicationDidBecomeActive_launchWithoutShortcut { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; - [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{}]; + XCTAssertTrue(launchResult, + @"didFinishLaunchingWithOptions must return true if not launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; OCMVerify(never(), [mockChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); } @@ -162,9 +164,9 @@ - (void)testApplicationDidBecomeActive_launchWithoutShortcut { - (void)testApplicationDidBecomeActive_launchWithShortcut { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -173,9 +175,10 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut { icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"search_the_thing.png"] userInfo:nil]; - [plugin application:[UIApplication sharedApplication] - didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; - + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + XCTAssertFalse(launchResult, + @"didFinishLaunchingWithOptions must return false if launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]); } @@ -183,9 +186,9 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut { - (void)testApplicationDidBecomeActive_launchWithShortcut_becomeActiveTwice { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -194,9 +197,10 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut_becomeActiveTwice { icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"search_the_thing.png"] userInfo:nil]; - [plugin application:[UIApplication sharedApplication] - didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; - + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + XCTAssertFalse(launchResult, + @"didFinishLaunchingWithOptions must return false if launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; // shortcut should only be handled once per launch. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m index f5b8b3405fc8..96fbf229e566 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m @@ -3,7 +3,6 @@ // found in the LICENSE file. @import quick_actions_ios; -@import quick_actions_ios.Test; @import XCTest; #import diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h deleted file mode 100644 index 63f615440dea..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h +++ /dev/null @@ -1,14 +0,0 @@ -// 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 Flutter; - -@interface FLTQuickActionsPlugin : NSObject - -/// Unavailable. -- (instancetype)init NS_UNAVAILABLE; - -/// Unavailable. -+ (instancetype)new NS_UNAVAILABLE; -@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m deleted file mode 100644 index fb8994322a9b..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m +++ /dev/null @@ -1,99 +0,0 @@ -// 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 "FLTQuickActionsPlugin.h" -#import "FLTQuickActionsPlugin_Test.h" -#import "FLTShortcutStateManager.h" - -static NSString *const kChannelName = @"plugins.flutter.io/quick_actions_ios"; - -@interface FLTQuickActionsPlugin () -@property(nonatomic, strong) FlutterMethodChannel *channel; -/// The type of the shortcut item selected when launching the app. -@property(nonatomic, strong, nullable) NSString *launchingShortcutType; -@property(nonatomic, strong) FLTShortcutStateManager *shortcutStateManager; -@end - -@implementation FLTQuickActionsPlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:kChannelName - binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = - [[FLTQuickActionsPlugin alloc] initWithChannel:channel - shortcutStateManager:[[FLTShortcutStateManager alloc] init]]; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; -} - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel - shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager { - if ((self = [super init])) { - _channel = channel; - _shortcutStateManager = shortcutStateManager; - } - return self; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"setShortcutItems"]) { - [self.shortcutStateManager setShortcutItems:call.arguments]; - result(nil); - } else if ([call.method isEqualToString:@"clearShortcutItems"]) { - [self.shortcutStateManager setShortcutItems:@[]]; - result(nil); - } else if ([call.method isEqualToString:@"getLaunchAction"]) { - result(nil); - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)dealloc { - [_channel setMethodCallHandler:nil]; - _channel = nil; -} - -- (BOOL)application:(UIApplication *)application - performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem - completionHandler:(void (^)(BOOL succeeded))completionHandler - API_AVAILABLE(ios(9.0)) { - [self handleShortcut:shortcutItem.type]; - return YES; -} - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - UIApplicationShortcutItem *shortcutItem = - launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; - if (shortcutItem) { - // Keep hold of the shortcut type and handle it in the - // `applicationDidBecomeActure:` method once the Dart MethodChannel - // is initialized. - self.launchingShortcutType = shortcutItem.type; - - // Return NO to indicate we handled the quick action to ensure - // the `application:performActionFor:` method is not called (as - // per Apple's documentation: - // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622935-application?language=objc). - return NO; - } - return YES; -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - if (self.launchingShortcutType) { - [self handleShortcut:self.launchingShortcutType]; - self.launchingShortcutType = nil; - } -} - -#pragma mark Private functions - -- (void)handleShortcut:(NSString *)shortcut { - [self.channel invokeMethod:@"launch" arguments:shortcut]; -} - -@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.h similarity index 100% rename from packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h rename to packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.h diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h deleted file mode 100644 index 514d0633f198..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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 Flutter; -#import "FLTShortcutStateManager.h" - -NS_ASSUME_NONNULL_BEGIN - -/// APIs exposed for unit tests. -@interface FLTQuickActionsPlugin () - -/// Initializes a FLTQuickActionsPlugin with the given method channel. -/// API exposed for unit tests. -/// @param channel A method channel. -/// @param shortcutStateManager An FLTShortcutStateManager that manages shortcut related states. -/// @return The initialized FLTQuickActionsPlugin. -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel - shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap deleted file mode 100644 index 3f7d7ce08203..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap +++ /dev/null @@ -1,11 +0,0 @@ -framework module quick_actions_ios { - umbrella header "quick_actions_ios-umbrella.h" - - export * - module * { export * } - - explicit module Test { - header "FLTQuickActionsPlugin_Test.h" - header "FLTShortcutStateManager.h" - } -} diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift new file mode 100644 index 000000000000..26d6d20f8c02 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift @@ -0,0 +1,90 @@ +// 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 Flutter + +public final class QuickActionsPlugin: NSObject, FlutterPlugin { + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/quick_actions_ios", + binaryMessenger: registrar.messenger()) + let instance = QuickActionsPlugin(channel: channel) + registrar.addMethodCallDelegate(instance, channel: channel) + registrar.addApplicationDelegate(instance) + } + + private let channel: FlutterMethodChannel + private let shortcutStateManager: FLTShortcutStateManager + /// The type of the shortcut item selected when launching the app. + private var launchingShortcutType: String? = nil + + // TODO: (hellohuanlin) remove `@objc` attribute and make it non-public after migrating tests to Swift. + @objc + public init( + channel: FlutterMethodChannel, + shortcutStateManager: FLTShortcutStateManager = FLTShortcutStateManager() + ) { + self.channel = channel + self.shortcutStateManager = shortcutStateManager + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "setShortcutItems": + // `arguments` must be an array of dictionaries + let items = call.arguments as! [[String: Any]] + shortcutStateManager.setShortcutItems(items) + result(nil) + case "clearShortcutItems": + shortcutStateManager.setShortcutItems([]) + result(nil) + case "getLaunchAction": + result(nil) + case _: + result(FlutterMethodNotImplemented) + } + } + + public func application( + _ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, + completionHandler: @escaping (Bool) -> Void + ) -> Bool { + handleShortcut(shortcutItem.type) + return true + } + + public func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [AnyHashable: Any] = [:] + ) -> Bool { + if let shortcutItem = launchOptions[UIApplication.LaunchOptionsKey.shortcutItem] + as? UIApplicationShortcutItem + { + // Keep hold of the shortcut type and handle it in the + // `applicationDidBecomeActive:` method once the Dart MethodChannel + // is initialized. + launchingShortcutType = shortcutItem.type + + // Return false to indicate we handled the quick action to ensure + // the `application:performActionFor:` method is not called (as + // per Apple's documentation: + // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622935-application). + return false + } + return true + } + + public func applicationDidBecomeActive(_ application: UIApplication) { + if let shortcutType = launchingShortcutType { + handleShortcut(shortcutType) + launchingShortcutType = nil + } + } + + private func handleShortcut(_ shortcut: String) { + channel.invokeMethod("launch", arguments: shortcut) + } + +} diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h b/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h deleted file mode 100644 index d099a0411cf0..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h +++ /dev/null @@ -1,10 +0,0 @@ -// 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 - -FOUNDATION_EXPORT double quickActionsIOSVersionNumber; -FOUNDATION_EXPORT const unsigned char quickActionsIOSVersionString[]; - -#import diff --git a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec index 68eaa6ff7dc9..d8090caa8ef6 100644 --- a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec @@ -14,11 +14,14 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/quick_actions' } s.documentation_url = 'https://pub.dev/packages/quick_actions' - s.source_files = 'Classes/**/*.{h,m}' - s.public_header_files = 'Classes/*.h' - s.private_header_files = 'Classes/PrivateHeaders/*.h' + s.swift_version = '5.0' + s.source_files = 'Classes/**/*.{h,m,swift}' + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } + s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.module_map = 'Classes/QuickActionsPlugin.modulemap' end diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 82fd25f06b2c..f01ae4aed9c3 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.15.0 <3.0.0" @@ -13,7 +13,7 @@ flutter: implements: quick_actions platforms: ios: - pluginClass: FLTQuickActionsPlugin + pluginClass: QuickActionsPlugin dartPluginClass: QuickActionsIos dependencies: From 8a8e67a7ec86ddf7d917442fe7cc438797e101da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Sep 2022 00:01:10 +0000 Subject: [PATCH 42/49] [gh_actions]: Bump github/codeql-action from 2.1.22 to 2.1.24 (#6451) --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 3bcfdad7fb6b..6650853754af 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b + uses: github/codeql-action/upload-sarif@904260d7d935dff982205cbdb42025ce30b7a34f with: sarif_file: results.sarif From 88dc5e33a327787a89b0e014425e8f2c528cfe10 Mon Sep 17 00:00:00 2001 From: Jami Couch Date: Thu, 22 Sep 2022 19:02:31 -0500 Subject: [PATCH 43/49] [google_sign_in] Fix issue obtaining serverAuthCode on Android and add forceCodeForRefreshToken parameter (#3356) --- AUTHORS | 1 + packages/google_sign_in/google_sign_in/AUTHORS | 1 + packages/google_sign_in/google_sign_in/CHANGELOG.md | 4 +++- .../google_sign_in/lib/google_sign_in.dart | 8 ++++++++ packages/google_sign_in/google_sign_in/pubspec.yaml | 6 +++--- .../google_sign_in/test/google_sign_in_test.dart | 10 ++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 31402c79d54a..3112c3b3fd05 100644 --- a/AUTHORS +++ b/AUTHORS @@ -68,3 +68,4 @@ Daniel Roek TheOneWithTheBraid Rulong Chen(陈汝龙) Hwanseok Kang +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in/AUTHORS b/packages/google_sign_in/google_sign_in/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in/AUTHORS +++ b/packages/google_sign_in/google_sign_in/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 286365ef9601..93497841fbd5 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 5.4.2 * Updates minimum Flutter version to 2.10. +* Adds override for `GoogleSignInPlatform.initWithParams`. +* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. ## 5.4.1 diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 135c422adfb5..3ae022306fe6 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -179,12 +179,16 @@ class GoogleSignIn { /// The [hostedDomain] argument specifies a hosted domain restriction. By /// setting this, sign in will be restricted to accounts of the user in the /// specified domain. By default, the list of accounts will not be restricted. + /// + /// The [forceCodeForRefreshToken] is used on Android to ensure the authentication + /// code can be exchanged for a refresh token after the first request. GoogleSignIn({ this.signInOption = SignInOption.standard, this.scopes = const [], this.hostedDomain, this.clientId, this.serverClientId, + this.forceCodeForRefreshToken = false, }); /// Factory for creating default sign in user experience. @@ -250,6 +254,9 @@ class GoogleSignIn { /// server. final String? serverClientId; + /// Force the authorization code to be valid for a refresh token every time. Only needed on Android. + final bool forceCodeForRefreshToken; + final StreamController _currentUserController = StreamController.broadcast(); @@ -286,6 +293,7 @@ class GoogleSignIn { hostedDomain: hostedDomain, clientId: clientId, serverClientId: serverClientId, + forceCodeForRefreshToken: forceCodeForRefreshToken, )) ..catchError((dynamic _) { // Invalidate initialization if it errors out. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 50339902fe75..c32dee78468b 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.4.1 +version: 5.4.2 environment: @@ -23,8 +23,8 @@ flutter: dependencies: flutter: sdk: flutter - google_sign_in_android: ^6.0.0 - google_sign_in_ios: ^5.4.0 + google_sign_in_android: ^6.1.0 + google_sign_in_ios: ^5.5.0 google_sign_in_platform_interface: ^2.2.0 google_sign_in_web: ^0.10.0 diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index b8676bda298e..b8a596b02065 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -80,6 +80,16 @@ void main() { verify(mockPlatform.signIn()); }); + test('forceCodeForRefreshToken sent with init method call', () async { + final GoogleSignIn googleSignIn = + GoogleSignIn(forceCodeForRefreshToken: true); + + await googleSignIn.signIn(); + + _verifyInit(mockPlatform, forceCodeForRefreshToken: true); + verify(mockPlatform.signIn()); + }); + test('signOut', () async { final GoogleSignIn googleSignIn = GoogleSignIn(); From 446c6f7ff79c845c87b527a93264ca246eef3555 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:03:37 -0700 Subject: [PATCH 44/49] [video_player]fix ios 16 bug where encrypted video stream is not showing (#6442) --- .../video_player_avfoundation/CHANGELOG.md | 3 +- .../ios/RunnerTests/VideoPlayerTests.m | 43 +++++++++++++ .../ios/RunnerUITests/VideoPlayerUITests.m | 53 ++++++++++++++- .../example/lib/main.dart | 64 ++++++++++++++++++- .../ios/Classes/FLTVideoPlayerPlugin.m | 26 ++++++++ .../video_player_avfoundation/pubspec.yaml | 2 +- 6 files changed, 185 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 3c9cc2d371fd..cc4411c8c651 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.6 +* Fixes a bug in iOS 16 where videos from protected live streams are not shown. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 7decd04bd168..813fca2b8e7d 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -11,6 +11,10 @@ @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; +// This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank +// video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the +// protection of pixel buffers in those streams. +@property(readonly, nonatomic) AVPlayerLayer *playerLayer; @end @interface FLTVideoPlayerPlugin (Test) @@ -61,6 +65,45 @@ @interface VideoPlayerTests : XCTestCase @implementation VideoPlayerTests +- (void)testIOS16BugWithEncryptedVideoStream { + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the + // protection of pixel buffers in those streams. + // Note that a better unit test is to validate that `copyPixelBuffer` API returns the pixel + // buffers as expected, which requires setting up the video player properly with a protected video + // stream (.m3u8 file). + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = + [registry registrarForPlugin:@"testPlayerLayerWorkaround"]; + FLTVideoPlayerPlugin *videoPlayerPlugin = + [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + FlutterError *error; + [videoPlayerPlugin initialize:&error]; + XCTAssertNil(error); + + FLTCreateMessage *create = [FLTCreateMessage + makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + packageName:nil + formatHint:nil + httpHeaders:@{}]; + FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(textureMessage); + FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureMessage.textureId]; + XCTAssertNotNil(player); + + if (@available(iOS 16.0, *)) { + XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present for iOS 16."); + XCTAssertNotNil(player.playerLayer.superlayer, + @"AVPlayerLayer should be added on screen for iOS 16."); + } else { + XCTAssertNil(player.playerLayer, @"AVPlayerLayer should not be present before iOS 16."); + } +} + - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { NSObject *mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index b9f0f16bb27b..531d4fdf213c 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -4,6 +4,7 @@ @import os.log; @import XCTest; +@import CoreGraphics; @interface VideoPlayerUITests : XCTestCase @property(nonatomic, strong) XCUIApplication *app; @@ -46,7 +47,7 @@ - (void)testPlayVideo { XCTAssertTrue(foundPlaybackSpeed5x); // Cycle through tabs. - for (NSString *tabName in @[ @"Asset", @"Remote" ]) { + for (NSString *tabName in @[ @"Asset mp4", @"Remote mp4" ]) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; XCUIElement *unselectedTab = [app.staticTexts elementMatchingPredicate:predicate]; XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); @@ -60,4 +61,54 @@ - (void)testPlayVideo { } } +- (void)testEncryptedVideoStream { + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. + + NSString *tabName = @"Remote enc m3u8"; + + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; + XCUIElement *unselectedTab = [self.app.staticTexts elementMatchingPredicate:predicate]; + XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); + XCTAssertFalse(unselectedTab.isSelected); + [unselectedTab tap]; + + XCUIElement *selectedTab = [self.app.otherElements + elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]]; + XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]); + XCTAssertTrue(selectedTab.isSelected); + + // Wait until the video is loaded. + [NSThread sleepForTimeInterval:60]; + + NSMutableSet *frames = [NSMutableSet set]; + int numberOfFrames = 60; + for (int i = 0; i < numberOfFrames; i++) { + UIImage *image = self.app.screenshot.image; + + // Plugin CI does not support attaching screenshot. + // Convert the image to base64 encoded string, and print it out for debugging purpose. + // NSLog truncates long strings, so need to scale downn image. + CGSize smallerSize = CGSizeMake(100, 200); + UIGraphicsBeginImageContextWithOptions(smallerSize, NO, 0.0); + [image drawInRect:CGRectMake(0, 0, smallerSize.width, smallerSize.height)]; + UIImage *smallerImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + // 0.5 compression is good enough for debugging purpose. + NSData *imageData = UIImageJPEGRepresentation(smallerImage, 0.5); + NSString *imageString = [imageData base64EncodedStringWithOptions:0]; + NSLog(@"frame %d image data:\n%@", i, imageString); + + [frames addObject:imageString]; + + // The sample interval must NOT be the same as video length. + // Otherwise it would always result in the same frame. + [NSThread sleepForTimeInterval:1]; + } + + // At least 1 loading and 2 distinct frames (3 in total) to validate that the video is playing. + XCTAssert(frames.count >= 3, @"Must have at least 3 distinct frames."); +} + @end diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index bca4e291efff..d385fd0ee66a 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -20,7 +20,7 @@ class _App extends StatelessWidget { @override Widget build(BuildContext context) { return DefaultTabController( - length: 2, + length: 3, child: Scaffold( key: const ValueKey('home_page'), appBar: AppBar( @@ -30,15 +30,20 @@ class _App extends StatelessWidget { tabs: [ Tab( icon: Icon(Icons.cloud), - text: 'Remote', + text: 'Remote mp4', ), - Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), + Tab( + icon: Icon(Icons.favorite), + text: 'Remote enc m3u8', + ), + Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset mp4'), ], ), ), body: TabBarView( children: [ _BumbleBeeRemoteVideo(), + _BumbleBeeEncryptedLiveStream(), _ButterFlyAssetVideo(), ], ), @@ -156,6 +161,59 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { } } +class _BumbleBeeEncryptedLiveStream extends StatefulWidget { + @override + _BumbleBeeEncryptedLiveStreamState createState() => + _BumbleBeeEncryptedLiveStreamState(); +} + +class _BumbleBeeEncryptedLiveStreamState + extends State<_BumbleBeeEncryptedLiveStream> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.network( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/encrypted_bee.m3u8', + ); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize(); + + _controller.play(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container(padding: const EdgeInsets.only(top: 20.0)), + const Text('With remote encrypted m3u8'), + Container( + padding: const EdgeInsets.all(20), + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Text('loading...'), + ), + ], + ), + ); + } +} + class _ControlsOverlay extends StatelessWidget { const _ControlsOverlay({Key? key, required this.controller}) : super(key: key); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index a95779b1cbab..645c86d6eade 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -36,6 +36,8 @@ - (void)onDisplayLink:(CADisplayLink *)link { @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @property(readonly, nonatomic) AVPlayerItemVideoOutput *videoOutput; +/// An invisible player layer used to access the pixel buffers in protected video streams in iOS 16. +@property(readonly, nonatomic) AVPlayerLayer *playerLayer; @property(readonly, nonatomic) CADisplayLink *displayLink; @property(nonatomic) FlutterEventChannel *eventChannel; @property(nonatomic) FlutterEventSink eventSink; @@ -132,6 +134,19 @@ NS_INLINE CGFloat radiansToDegrees(CGFloat radians) { return degrees; }; +NS_INLINE UIViewController *rootViewController() API_AVAILABLE(ios(16.0)) { + for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { + if ([scene isKindOfClass:UIWindowScene.class]) { + for (UIWindow *window in ((UIWindowScene *)scene).windows) { + if (window.isKeyWindow) { + return window.rootViewController; + } + } + } + } + return nil; +} + - (AVMutableVideoComposition *)getVideoCompositionWithTransform:(CGAffineTransform)transform withAsset:(AVAsset *)asset withVideoTrack:(AVAssetTrack *)videoTrack { @@ -227,6 +242,14 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item _player = [AVPlayer playerWithPlayerItem:item]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the + // protection of pixel buffers in those streams. + if (@available(iOS 16.0, *)) { + _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; + [rootViewController().view.layer addSublayer:_playerLayer]; + } + [self createVideoOutputAndDisplayLink:frameUpdater]; [self addObservers:item]; @@ -458,6 +481,9 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments /// so the channel is going to die or is already dead. - (void)disposeSansEventChannel { _disposed = YES; + if (@available(iOS 16.0, *)) { + [_playerLayer removeFromSuperlayer]; + } [_displayLink invalidate]; AVPlayerItem *currentItem = self.player.currentItem; [currentItem removeObserver:self forKeyPath:@"status"]; diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 06042c34bad6..bd88ddf94876 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.5 +version: 2.3.6 environment: sdk: ">=2.14.0 <3.0.0" From 3ba81503e7f71c5896ee2c1645f225086c082704 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 19:24:41 -0700 Subject: [PATCH 45/49] [video_player]remove integration tests (#6471) --- .../ios/RunnerUITests/VideoPlayerUITests.m | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 531d4fdf213c..54c97030c3ae 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -61,54 +61,4 @@ - (void)testPlayVideo { } } -- (void)testEncryptedVideoStream { - // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank - // video for encrypted video streams. - - NSString *tabName = @"Remote enc m3u8"; - - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; - XCUIElement *unselectedTab = [self.app.staticTexts elementMatchingPredicate:predicate]; - XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); - XCTAssertFalse(unselectedTab.isSelected); - [unselectedTab tap]; - - XCUIElement *selectedTab = [self.app.otherElements - elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]]; - XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]); - XCTAssertTrue(selectedTab.isSelected); - - // Wait until the video is loaded. - [NSThread sleepForTimeInterval:60]; - - NSMutableSet *frames = [NSMutableSet set]; - int numberOfFrames = 60; - for (int i = 0; i < numberOfFrames; i++) { - UIImage *image = self.app.screenshot.image; - - // Plugin CI does not support attaching screenshot. - // Convert the image to base64 encoded string, and print it out for debugging purpose. - // NSLog truncates long strings, so need to scale downn image. - CGSize smallerSize = CGSizeMake(100, 200); - UIGraphicsBeginImageContextWithOptions(smallerSize, NO, 0.0); - [image drawInRect:CGRectMake(0, 0, smallerSize.width, smallerSize.height)]; - UIImage *smallerImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - // 0.5 compression is good enough for debugging purpose. - NSData *imageData = UIImageJPEGRepresentation(smallerImage, 0.5); - NSString *imageString = [imageData base64EncodedStringWithOptions:0]; - NSLog(@"frame %d image data:\n%@", i, imageString); - - [frames addObject:imageString]; - - // The sample interval must NOT be the same as video length. - // Otherwise it would always result in the same frame. - [NSThread sleepForTimeInterval:1]; - } - - // At least 1 loading and 2 distinct frames (3 in total) to validate that the video is playing. - XCTAssert(frames.count >= 3, @"Must have at least 3 distinct frames."); -} - @end From 97d48f4cb610b4f1346e38d5a9919de145be20e6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 23 Sep 2022 11:50:05 -0400 Subject: [PATCH 46/49] Roll Flutter from cf01ecd19e61 to b31f41bd1aa6 (50 revisions) (#6475) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 427e69ccf7d7..6db47f1c12cd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cf01ecd19e61b436c6d7f9c0f6d57ef84aeb0257 +b31f41bd1aa68531d30854937ac004d2f4ab2361 From 376a64fe7d27c18d6cf9c9750f5a3c4067b0e558 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 23 Sep 2022 17:38:04 -0400 Subject: [PATCH 47/49] [tools] Add 'run_tests.sh' to the dev-only list (#6474) --- script/tool/CHANGELOG.md | 3 +- .../lib/src/common/package_state_utils.dart | 3 ++ script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 1 + .../tool/test/version_check_command_test.dart | 32 +++++++++++++++++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index f6dfb0e58266..2417d0fa833c 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0+1 +* Recognizes `run_test.sh` as a developer-only file in `version-check`. * Adds `readme-check` validation that the example/README.md for a federated plugin's implementation packages has a warning about the intended use of the example instead of the template boilerplate. diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index 1cff65bb6b0c..c9465876f290 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -168,6 +168,9 @@ Future _isDevChange(List pathComponents, // The top-level "tool" directory is for non-client-facing utility // code, such as test scripts. pathComponents.first == 'tool' || + // Entry point for the 'custom-test' command, which is only for CI and + // local testing. + pathComponents.first == 'run_tests.sh' || // Ignoring lints doesn't affect clients. pathComponents.contains('lint-baseline.xml') || await _isGradleTestDependencyChange(pathComponents, diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index e51c7433aa5c..9b9d73288552 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.10.0 +version: 0.10.0+1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart index 63ac1802e70c..c20951876e39 100644 --- a/script/tool/test/common/package_state_utils_test.dart +++ b/script/tool/test/common/package_state_utils_test.dart @@ -66,6 +66,7 @@ void main() { 'packages/a_plugin/example/ios/RunnerTests/Foo.m', 'packages/a_plugin/example/ios/RunnerUITests/info.plist', 'packages/a_plugin/tool/a_development_tool.dart', + 'packages/a_plugin/run_tests.sh', 'packages/a_plugin/CHANGELOG.md', ]; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 2ff48d618258..0e94712e9ef0 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -1059,6 +1059,38 @@ packages/plugin/android/build.gradle ]), ); }); + + test('allows missing CHANGELOG and version change for dev-only changes', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + // File list. + MockProcess(stdout: ''' +packages/plugin/tool/run_tests.dart +packages/plugin/run_tests.sh +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); }); test('allows valid against pub', () async { From b4e53b337c6dbfb1e606f77184a0f0ead79a2d5e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 24 Sep 2022 11:50:35 -0400 Subject: [PATCH 48/49] Roll Flutter from b31f41bd1aa6 to 07665fba77bc (22 revisions) (#6479) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6db47f1c12cd..f3d11c356984 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b31f41bd1aa68531d30854937ac004d2f4ab2361 +07665fba77bc6add0c729ea3c89e6f71e18f7110 From 45d6e0df271064d270bdd3b69053d3f719b51509 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 25 Sep 2022 11:50:39 -0400 Subject: [PATCH 49/49] Roll Flutter from 07665fba77bc to 64f84f6a47d2 (4 revisions) (#6480) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index f3d11c356984..886179e55e79 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -07665fba77bc6add0c729ea3c89e6f71e18f7110 +64f84f6a47d276e4d79cb10c203adbddeeeae336