From 2fd6c1b317f2c21a79ddec4e3f47144604805199 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 08:14:34 -0400 Subject: [PATCH 01/22] [ci] Switch some tests to LUCI Enables various new LUCI targets and removes the corresponding Cirrus versions: - The parts of `repo_checks` that have been migrated. - Android platform tests other than FTL. - Web platform tests. Since the Cirrus Android platform tests are now doing less work, the number of shards has been reduced slightly. Part of https://github.com/flutter/flutter/issues/114373 --- .ci.yaml | 17 --------------- .cirrus.yml | 59 ++++++----------------------------------------------- 2 files changed, 6 insertions(+), 70 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 0c75578116e..52f50f6d605 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -97,7 +97,6 @@ targets: version_file: flutter_master.version - name: Linux repo_checks - bringup: true # New target recipe: packages/packages timeout: 30 properties: @@ -282,7 +281,6 @@ targets: channel: stable - name: Linux_android android_platform_tests_shard_1 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -293,7 +291,6 @@ targets: package_sharding: "--shardIndex 0 --shardCount 6" - name: Linux_android android_platform_tests_shard_2 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -304,7 +301,6 @@ targets: package_sharding: "--shardIndex 1 --shardCount 6" - name: Linux_android android_platform_tests_shard_3 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -315,7 +311,6 @@ targets: package_sharding: "--shardIndex 2 --shardCount 6" - name: Linux_android android_platform_tests_shard_4 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -326,7 +321,6 @@ targets: package_sharding: "--shardIndex 3 --shardCount 6" - name: Linux_android android_platform_tests_shard_5 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -337,7 +331,6 @@ targets: package_sharding: "--shardIndex 4 --shardCount 6" - name: Linux_android android_platform_tests_shard_6 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -348,7 +341,6 @@ targets: package_sharding: "--shardIndex 6 --shardCount 6" - name: Linux_android android_platform_tests_shard_1 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -360,7 +352,6 @@ targets: package_sharding: "--shardIndex 0 --shardCount 6" - name: Linux_android android_platform_tests_shard_2 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -372,7 +363,6 @@ targets: package_sharding: "--shardIndex 1 --shardCount 6" - name: Linux_android android_platform_tests_shard_3 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -384,7 +374,6 @@ targets: package_sharding: "--shardIndex 2 --shardCount 6" - name: Linux_android android_platform_tests_shard_4 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -396,7 +385,6 @@ targets: package_sharding: "--shardIndex 3 --shardCount 6" - name: Linux_android android_platform_tests_shard_5 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -408,7 +396,6 @@ targets: package_sharding: "--shardIndex 4 --shardCount 6" - name: Linux_android android_platform_tests_shard_6 stable - bringup: true # New target recipe: packages/packages presubmit: false timeout: 60 @@ -438,7 +425,6 @@ targets: channel: stable - name: Linux_web web_platform_tests_shard_1 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -449,7 +435,6 @@ targets: package_sharding: "--shardIndex 0 --shardCount 2" - name: Linux_web web_platform_tests_shard_2 master - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -460,7 +445,6 @@ targets: package_sharding: "--shardIndex 1 --shardCount 2" - name: Linux_web web_platform_tests_shard_1 stable - bringup: true # New target recipe: packages/packages timeout: 60 properties: @@ -471,7 +455,6 @@ targets: package_sharding: "--shardIndex 0 --shardCount 2" - name: Linux_web web_platform_tests_shard_2 stable - bringup: true # New target recipe: packages/packages timeout: 60 properties: diff --git a/.cirrus.yml b/.cirrus.yml index b995936fdc0..dfe9ae96659 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -65,20 +65,6 @@ task: # (on Flutter master). - name: repo_checks always: - format_script: ./script/tool_runner.sh format --fail-on-change - license_script: $PLUGIN_TOOL_COMMAND license-check - # The major and minor version here should match the lowest version - # analyzed in legacy_version_analyze. - pubspec_script: ./script/tool_runner.sh pubspec-check --min-min-flutter-version=3.3.0 --allow-dependencies=script/configs/allowed_unpinned_deps.yaml --allow-pinned-dependencies=script/configs/allowed_pinned_deps.yaml - readme_script: - - ./script/tool_runner.sh readme-check - # Re-run with --require-excerpts, skipping packages that still need - # to be converted. Once https://github.com/flutter/flutter/issues/102679 - # has been fixed, this can be removed and there can just be a single - # run with --require-excerpts and no exclusions. - - ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml - dependabot_script: $PLUGIN_TOOL_COMMAND dependabot-check - gradle_script: $PLUGIN_TOOL_COMMAND gradle-check version_script: # For pre-submit, pass the PR labels to the script to allow for # check overrides. @@ -90,7 +76,6 @@ task: - else - ./script/tool_runner.sh version-check --check-for-missing-changes --pr-labels="$CIRRUS_PR_LABELS" - fi - publishable_script: ./script/tool_runner.sh publish-check --allow-pre-release federated_safety_script: # This check is only meaningful for PRs, as it validates changes # rather than state. @@ -173,28 +158,17 @@ task: skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: matrix: - PACKAGE_SHARDING: "--shardIndex 0 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 1 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 2 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 3 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 4 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 5 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 6 --shardCount 8" - PACKAGE_SHARDING: "--shardIndex 7 --shardCount 8" + PACKAGE_SHARDING: "--shardIndex 0 --shardCount 6" + PACKAGE_SHARDING: "--shardIndex 1 --shardCount 6" + PACKAGE_SHARDING: "--shardIndex 2 --shardCount 6" + PACKAGE_SHARDING: "--shardIndex 3 --shardCount 6" + PACKAGE_SHARDING: "--shardIndex 4 --shardCount 6" + PACKAGE_SHARDING: "--shardIndex 5 --shardCount 6" matrix: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[d6583b08f79f91ea4844c77460f04539965e46ad2fd97fb7c062b4dfe88016228b86ebe8c220ab4187e0c4bd773dc1e7] GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1a2eebf9367197bbe812d9a0ea83a53a05aeba4bb5e4964fe6a69727883cd87e51238d39237b1f80b0894c48419ac268] - build_script: - - ./script/tool_runner.sh build-examples --apk - lint_script: - - ./script/tool_runner.sh lint-android # must come after build-examples - native_unit_test_script: - # Native integration tests are handled by Firebase Test Lab below, so - # only run unit tests. - # Must come after build-examples. - - ./script/tool_runner.sh native-test --android --no-integration --exclude script/configs/exclude_native_unit_android.yaml firebase_test_lab_script: - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json @@ -202,28 +176,7 @@ task: - else - echo "This user does not have permission to run Firebase Test Lab tests." - fi - # Upload the full lint results to Cirrus to display in the results UI. - always: - android-lint_artifacts: - path: "**/reports/lint-results-debug.xml" - type: text/xml - format: android-lint ### Web tasks ### - - name: web-platform_tests - env: - matrix: - PACKAGE_SHARDING: "--shardIndex 0 --shardCount 2" - PACKAGE_SHARDING: "--shardIndex 1 --shardCount 2" - matrix: - CHANNEL: "master" - CHANNEL: "stable" - << : *INSTALL_CHROME_LINUX - chromedriver_background_script: - - $CHROMEDRIVER_EXECUTABLE --port=4444 - build_script: - - ./script/tool_runner.sh build-examples --web - drive_script: - - ./script/tool_runner.sh drive-examples --web --exclude=script/configs/exclude_integration_web.yaml - name: web_benchmarks_test env: matrix: From 2d98156803d1dba54b2e481ac4c4240d0ff9128b Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 09:30:39 -0400 Subject: [PATCH 02/22] Fix shard index --- .ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 52f50f6d605..b507e185c66 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -338,7 +338,7 @@ targets: channel: master version_file: flutter_master.version cores: "32" - package_sharding: "--shardIndex 6 --shardCount 6" + package_sharding: "--shardIndex 5 --shardCount 6" - name: Linux_android android_platform_tests_shard_1 stable recipe: packages/packages @@ -404,7 +404,7 @@ targets: channel: stable version_file: flutter_stable.version cores: "32" - package_sharding: "--shardIndex 6 --shardCount 6" + package_sharding: "--shardIndex 5 --shardCount 6" ### Web tasks ### - name: Linux_web web_build_all_packages master From 4a553a632cc41a2154b5a7030aa74ae09e8d0b94 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 11:48:56 -0400 Subject: [PATCH 03/22] Skip flaky test on web --- .../example/integration_test/video_player_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 6ca4de94312..b6c592e54e2 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -142,6 +142,8 @@ void main() { expect(controller.value.isPlaying, false); expect(controller.value.position, tenMillisBeforeEnd); }, + // Flaky on web: https://github.com/flutter/flutter/issues/130147 + skip: kIsWeb, ); testWidgets( From 548e6dad2d60a7ba6892fecaad395225247303fb Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 12:06:09 -0400 Subject: [PATCH 04/22] Undo migration of Android unit tests --- .ci/targets/android_platform_tests.yaml | 8 +++++--- .cirrus.yml | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.ci/targets/android_platform_tests.yaml b/.ci/targets/android_platform_tests.yaml index 1944cdecc9d..ee5de6e56b1 100644 --- a/.ci/targets/android_platform_tests.yaml +++ b/.ci/targets/android_platform_tests.yaml @@ -11,9 +11,11 @@ tasks: # different exclusions. # TODO(stuartmorgan): Eliminate the native unit test exclusion, and combine # these steps. - - name: native unit tests - script: script/tool_runner.sh - args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] + # TODO(stuartmorgan): Enable this once https://github.com/flutter/flutter/issues/130148 + # is resolved. + #- name: native unit tests + # script: script/tool_runner.sh + # args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] # TODO(stuartmorgan): Enable these once # https://github.com/flutter/flutter/issues/120736 is implemented. # See also https://github.com/flutter/flutter/issues/114373 diff --git a/.cirrus.yml b/.cirrus.yml index dfe9ae96659..c3111708ca7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -169,6 +169,10 @@ task: CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[d6583b08f79f91ea4844c77460f04539965e46ad2fd97fb7c062b4dfe88016228b86ebe8c220ab4187e0c4bd773dc1e7] GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1a2eebf9367197bbe812d9a0ea83a53a05aeba4bb5e4964fe6a69727883cd87e51238d39237b1f80b0894c48419ac268] + native_unit_test_script: + # Native integration tests are handled by Firebase Test Lab below, so + # only run unit tests. + - ./script/tool_runner.sh native-test --android --no-integration --exclude script/configs/exclude_native_unit_android.yaml firebase_test_lab_script: - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json From ff42883640c75e71e0c051826dd00fe1c67f57ba Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 14:32:08 -0400 Subject: [PATCH 05/22] Revert "Undo migration of Android unit tests" This reverts commit 548e6dad2d60a7ba6892fecaad395225247303fb. --- .ci/targets/android_platform_tests.yaml | 8 +++----- .cirrus.yml | 4 ---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.ci/targets/android_platform_tests.yaml b/.ci/targets/android_platform_tests.yaml index ee5de6e56b1..1944cdecc9d 100644 --- a/.ci/targets/android_platform_tests.yaml +++ b/.ci/targets/android_platform_tests.yaml @@ -11,11 +11,9 @@ tasks: # different exclusions. # TODO(stuartmorgan): Eliminate the native unit test exclusion, and combine # these steps. - # TODO(stuartmorgan): Enable this once https://github.com/flutter/flutter/issues/130148 - # is resolved. - #- name: native unit tests - # script: script/tool_runner.sh - # args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] + - name: native unit tests + script: script/tool_runner.sh + args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] # TODO(stuartmorgan): Enable these once # https://github.com/flutter/flutter/issues/120736 is implemented. # See also https://github.com/flutter/flutter/issues/114373 diff --git a/.cirrus.yml b/.cirrus.yml index c3111708ca7..dfe9ae96659 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -169,10 +169,6 @@ task: CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[d6583b08f79f91ea4844c77460f04539965e46ad2fd97fb7c062b4dfe88016228b86ebe8c220ab4187e0c4bd773dc1e7] GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1a2eebf9367197bbe812d9a0ea83a53a05aeba4bb5e4964fe6a69727883cd87e51238d39237b1f80b0894c48419ac268] - native_unit_test_script: - # Native integration tests are handled by Firebase Test Lab below, so - # only run unit tests. - - ./script/tool_runner.sh native-test --android --no-integration --exclude script/configs/exclude_native_unit_android.yaml firebase_test_lab_script: - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json From b4a89529190882e6ac5dd55703ae5de878956c11 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 7 Jul 2023 14:33:30 -0400 Subject: [PATCH 06/22] Switch to JDK 12 for Android --- .ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci.yaml b/.ci.yaml index b507e185c66..1bbe3e2795f 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -26,7 +26,7 @@ platform_properties: dependencies: >- [ {"dependency": "android_sdk", "version": "version:33v6"}, - {"dependency": "open_jdk", "version": "version:11"}, + {"dependency": "open_jdk", "version": "version:12"}, {"dependency": "curl", "version": "version:7.64.0"} ] linux_desktop: From 9995ab1ba28f519a45533fc3513c7f00710c9128 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 11 Jul 2023 15:36:08 -0400 Subject: [PATCH 07/22] Enable LUCI test --- .ci/targets/android_platform_tests.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.ci/targets/android_platform_tests.yaml b/.ci/targets/android_platform_tests.yaml index ee5de6e56b1..1944cdecc9d 100644 --- a/.ci/targets/android_platform_tests.yaml +++ b/.ci/targets/android_platform_tests.yaml @@ -11,11 +11,9 @@ tasks: # different exclusions. # TODO(stuartmorgan): Eliminate the native unit test exclusion, and combine # these steps. - # TODO(stuartmorgan): Enable this once https://github.com/flutter/flutter/issues/130148 - # is resolved. - #- name: native unit tests - # script: script/tool_runner.sh - # args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] + - name: native unit tests + script: script/tool_runner.sh + args: ["native-test", "--android", "--no-integration", "--exclude=script/configs/exclude_native_unit_android.yaml"] # TODO(stuartmorgan): Enable these once # https://github.com/flutter/flutter/issues/120736 is implemented. # See also https://github.com/flutter/flutter/issues/114373 From 78d7a5b9e7753353577db6554c7b30ee9beefc3a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 11 Jul 2023 16:11:39 -0400 Subject: [PATCH 08/22] Fix file_selector; don't use reflection in test --- .../FileSelectorApiImpl.java | 20 ++++++++++--- .../FileSelectorAndroidPluginTest.java | 29 ++----------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java index 523ea30f68b..31178fb992e 100644 --- a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java +++ b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java @@ -15,6 +15,8 @@ import android.provider.OpenableColumns; import android.util.Log; import android.webkit.MimeTypeMap; + +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -38,7 +40,8 @@ public class FileSelectorApiImpl implements GeneratedFileSelectorApi.FileSelecto // Request code for selecting a directory. private static final int OPEN_DIR = 223; - private final NativeObjectFactory objectFactory; + private final @NonNull NativeObjectFactory objectFactory; + private final @NonNull AndroidCapabilityChecker capabilityChecker; @Nullable ActivityPluginBinding activityPluginBinding; private abstract static class OnResultListener { @@ -60,16 +63,25 @@ DataInputStream newDataInputStream(InputStream inputStream) { } } + // Interface for an injectable provider that returns the current SDK version. + @VisibleForTesting + interface AndroidCapabilityChecker { + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.LOLLIPOP) + boolean supportsDirectorySelection(); + } + public FileSelectorApiImpl(@NonNull ActivityPluginBinding activityPluginBinding) { - this(activityPluginBinding, new NativeObjectFactory()); + this(activityPluginBinding, new NativeObjectFactory(), () -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); } @VisibleForTesting FileSelectorApiImpl( @NonNull ActivityPluginBinding activityPluginBinding, - @NonNull NativeObjectFactory objectFactory) { + @NonNull NativeObjectFactory objectFactory, + @NonNull AndroidCapabilityChecker capabilityChecker) { this.activityPluginBinding = activityPluginBinding; this.objectFactory = objectFactory; + this.capabilityChecker = capabilityChecker; } @Override @@ -171,7 +183,7 @@ public void onResult(int resultCode, @Nullable Intent data) { @Override public void getDirectoryPath( @Nullable String initialDirectory, @NonNull GeneratedFileSelectorApi.Result result) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) { + if (!capabilityChecker.supportsDirectorySelection()) { throw new UnsupportedOperationException( "Selecting a directory is only supported on versions >= 21"); } diff --git a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java index 5253fc578f8..8003c586b72 100644 --- a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java +++ b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java @@ -16,7 +16,6 @@ import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.provider.OpenableColumns; import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -24,11 +23,8 @@ import java.io.DataInputStream; import java.io.FileNotFoundException; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; -import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -69,23 +65,6 @@ private void mockContentResolver( when(mockResolver.openInputStream(uri)).thenReturn(mock(InputStream.class)); } - @SuppressWarnings("JavaReflectionMemberAccess") - private static void setFinalStatic( - Class classToModify, String fieldName, Object newValue) { - try { - Field field = classToModify.getField(fieldName); - field.setAccessible(true); - - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - field.set(null, newValue); - } catch (Exception e) { - Assert.fail("Unable to mock static field: " + fieldName); - } - } - @SuppressWarnings({"rawtypes", "unchecked"}) @Test public void openFileReturnsSuccessfully() throws FileNotFoundException { @@ -100,7 +79,7 @@ public void openFileReturnsSuccessfully() throws FileNotFoundException { when(mockActivity.getContentResolver()).thenReturn(mockContentResolver); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory); + new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.openFile( @@ -152,7 +131,7 @@ public void openFilesReturnsSuccessfully() throws FileNotFoundException { when(mockActivity.getContentResolver()).thenReturn(mockContentResolver); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory); + new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.openFiles( @@ -207,15 +186,13 @@ public void openFilesReturnsSuccessfully() throws FileNotFoundException { @SuppressWarnings({"rawtypes", "unchecked"}) @Test public void getDirectoryPathReturnsSuccessfully() { - setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); - final Uri mockUri = mock(Uri.class); when(mockUri.toString()).thenReturn("some/path/"); when(mockObjectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE)).thenReturn(mockIntent); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory); + new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.getDirectoryPath(null, mockResult); From ce4d5d4459bd53507b9fae7085cb0b8f65520151 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 08:02:10 -0400 Subject: [PATCH 09/22] Update camera to use abstract capability checker --- .../io/flutter/plugins/camera/Camera.java | 14 +++-- .../plugins/camera/CameraRegionUtils.java | 2 +- .../plugins/camera/SdkCapabilityChecker.java | 52 +++++++++++++++++++ .../flutter/plugins/camera/VideoRenderer.java | 2 +- .../noisereduction/NoiseReductionFeature.java | 5 +- .../resolution/ResolutionFeature.java | 5 +- .../features/zoomlevel/ZoomLevelFeature.java | 6 +-- .../camera/media/MediaRecorderBuilder.java | 4 +- ...raRegionUtils_getCameraBoundariesTest.java | 3 +- .../io/flutter/plugins/camera/CameraTest.java | 49 ++++++++++------- .../NoiseReductionFeatureTest.java | 11 ++-- .../zoomlevel/ZoomLevelFeatureTest.java | 14 ++--- 12 files changed, 111 insertions(+), 56 deletions(-) create mode 100644 packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 808051583f6..09fbd4408be 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -24,8 +24,6 @@ import android.media.Image; import android.media.ImageReader; import android.media.MediaRecorder; -import android.os.Build; -import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Handler; import android.os.HandlerThread; @@ -259,7 +257,7 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException { // TODO(camsim99): Revert changes that allow legacy code to be used when recordingProfile is null // once this has largely been fixed on the Android side. https://github.com/flutter/flutter/issues/119668 EncoderProfiles recordingProfile = getRecordingProfile(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && recordingProfile != null) { + if (SdkCapabilityChecker.supportsEncoderProfiles() && recordingProfile != null) { mediaRecorderBuilder = new MediaRecorderBuilder(recordingProfile, outputFilePath); } else { mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfileLegacy(), outputFilePath); @@ -469,7 +467,7 @@ public void onClosed(@NonNull CameraCaptureSession session) { }; // Start the session. - if (VERSION.SDK_INT >= VERSION_CODES.P) { + if (SdkCapabilityChecker.supportsSessionConfiguration()) { // Collect all surfaces to render to. List configs = new ArrayList<>(); configs.add(new OutputConfiguration(flutterSurface)); @@ -821,7 +819,7 @@ public void pauseVideoRecording(@NonNull final Result result) { } try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (SdkCapabilityChecker.supportsVideoPause()) { mediaRecorder.pause(); } else { result.error("videoRecordingFailed", "pauseVideoRecording requires Android API +24.", null); @@ -842,7 +840,7 @@ public void resumeVideoRecording(@NonNull final Result result) { } try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (SdkCapabilityChecker.supportsVideoPause()) { mediaRecorder.resume(); } else { result.error( @@ -1298,8 +1296,8 @@ public void setDescriptionWhileRecording( return; } - // See VideoRenderer.java requires API 26 to switch camera while recording - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { + // See VideoRenderer.java; support for this EGL extension is required to switch camera while recording. + if (!SdkCapabilityChecker.supportsEglRecordableAndroid()) { result.error( "setDescriptionWhileRecordingFailed", "Device does not support switching the camera while recording", diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java index 55ca63caed4..df9b06b8551 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java @@ -32,7 +32,7 @@ public final class CameraRegionUtils { @NonNull public static Size getCameraBoundaries( @NonNull CameraProperties cameraProperties, @NonNull CaptureRequest.Builder requestBuilder) { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + if (SdkCapabilityChecker.supportsDistortionCorrection() && supportsDistortionCorrection(cameraProperties)) { // Get the current distortion correction mode. Integer distortionCorrectionMode = diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java new file mode 100644 index 00000000000..0bed5181bd2 --- /dev/null +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camera; + +import android.annotation.SuppressLint; +import android.os.Build; +import androidx.annotation.ChecksSdkIntAtLeast; +import androidx.annotation.VisibleForTesting; + +public class SdkCapabilityChecker { + /** The current SDK version, overridable for testing. */ + @SuppressLint("AnnotateVersionCheck") + @VisibleForTesting + public static int SDK_VERSION = Build.VERSION.SDK_INT; + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) + public static boolean supportsDistortionCorrection() { + return SDK_VERSION >= Build.VERSION_CODES.P; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) + public static boolean supportsEglRecordableAndroid() { + return SDK_VERSION >= Build.VERSION_CODES.O; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) + public static boolean supportsEncoderProfiles() { + return SDK_VERSION >= Build.VERSION_CODES.S; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) + public static boolean supportsMarshmallowNoiseReductionModes() { + return SDK_VERSION >= Build.VERSION_CODES.M; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) + public static boolean supportsSessionConfiguration() { + return SDK_VERSION >= Build.VERSION_CODES.P; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) + public static boolean supportsVideoPause() { + return SDK_VERSION >= Build.VERSION_CODES.N; + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) + public static boolean supportsZoomRatio() { + return SDK_VERSION >= Build.VERSION_CODES.R; + } +} diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java index baa4157688e..35e8dd32b09 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/VideoRenderer.java @@ -167,7 +167,7 @@ void configureOpenGL() { "cannot configure OpenGL. missing EGL_ANDROID_presentation_time"); int[] attribList; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (SdkCapabilityChecker.supportsEglRecordableAndroid()) { attribList = new int[] { EGL14.EGL_RED_SIZE, 8, diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java index d98328dcce4..ae7c2262ea7 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java @@ -6,12 +6,11 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CaptureRequest; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import android.util.Log; import androidx.annotation.NonNull; import io.flutter.BuildConfig; import io.flutter.plugins.camera.CameraProperties; +import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; import java.util.HashMap; @@ -36,7 +35,7 @@ public NoiseReductionFeature(@NonNull CameraProperties cameraProperties) { NOISE_REDUCTION_MODES.put(NoiseReductionMode.fast, CaptureRequest.NOISE_REDUCTION_MODE_FAST); NOISE_REDUCTION_MODES.put( NoiseReductionMode.highQuality, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); - if (VERSION.SDK_INT >= VERSION_CODES.M) { + if (SdkCapabilityChecker.supportsMarshmallowNoiseReductionModes()) { NOISE_REDUCTION_MODES.put( NoiseReductionMode.minimal, CaptureRequest.NOISE_REDUCTION_MODE_MINIMAL); NOISE_REDUCTION_MODES.put( diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java index 4cba4481663..57ceddb3963 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java @@ -15,6 +15,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import io.flutter.plugins.camera.CameraProperties; +import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; import java.util.List; @@ -126,7 +127,7 @@ static Size computeBestPreviewSize(int cameraId, ResolutionPreset preset) if (preset.ordinal() > ResolutionPreset.high.ordinal()) { preset = ResolutionPreset.high; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SdkCapabilityChecker.supportsEncoderProfiles()) { EncoderProfiles profile = getBestAvailableCamcorderProfileForResolutionPreset(cameraId, preset); List videoProfiles = profile.getVideoProfiles(); @@ -268,7 +269,7 @@ private void configureResolution(ResolutionPreset resolutionPreset, int cameraId } boolean captureSizeCalculated = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SdkCapabilityChecker.supportsEncoderProfiles()) { recordingProfileLegacy = null; recordingProfile = getBestAvailableCamcorderProfileForResolutionPreset(cameraId, resolutionPreset); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java index dda06845da2..4110ef3f05c 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java @@ -7,9 +7,9 @@ import android.annotation.SuppressLint; import android.graphics.Rect; import android.hardware.camera2.CaptureRequest; -import android.os.Build; import androidx.annotation.NonNull; import io.flutter.plugins.camera.CameraProperties; +import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; /** Controls the zoom configuration on the {@link android.hardware.camera2} API. */ @@ -37,7 +37,7 @@ public ZoomLevelFeature(@NonNull CameraProperties cameraProperties) { return; } // On Android 11+ CONTROL_ZOOM_RATIO_RANGE should be use to get the zoom ratio directly as minimum zoom does not have to be 1.0f. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (SdkCapabilityChecker.supportsZoomRatio()) { minimumZoomLevel = cameraProperties.getScalerMinZoomRatio(); maximumZoomLevel = cameraProperties.getScalerMaxZoomRatio(); } else { @@ -83,7 +83,7 @@ public void updateBuilder(@NonNull CaptureRequest.Builder requestBuilder) { // On Android 11+ CONTROL_ZOOM_RATIO can be set to a zoom ratio and the camera feed will compute // how to zoom on its own accounting for multiple logical cameras. // Prior the image cropping window must be calculated and set manually. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (SdkCapabilityChecker.supportsZoomRatio()) { requestBuilder.set( CaptureRequest.CONTROL_ZOOM_RATIO, ZoomUtils.computeZoomRatio(currentSetting, minimumZoomLevel, maximumZoomLevel)); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java index 966019bb143..d41c3335f22 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java @@ -7,8 +7,8 @@ import android.media.CamcorderProfile; import android.media.EncoderProfiles; import android.media.MediaRecorder; -import android.os.Build; import androidx.annotation.NonNull; +import io.flutter.plugins.camera.SdkCapabilityChecker; import java.io.IOException; public class MediaRecorderBuilder { @@ -78,7 +78,7 @@ public MediaRecorder build() throws IOException, NullPointerException, IndexOutO if (enableAudio) mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && encoderProfiles != null) { + if (SdkCapabilityChecker.supportsEncoderProfiles() && encoderProfiles != null) { EncoderProfiles.VideoProfile videoProfile = encoderProfiles.getVideoProfiles().get(0); EncoderProfiles.AudioProfile audioProfile = encoderProfiles.getAudioProfiles().get(0); diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java index 4c0164981b7..b88765d2f2f 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java @@ -15,7 +15,6 @@ import android.hardware.camera2.CaptureRequest; import android.os.Build; import android.util.Size; -import io.flutter.plugins.camera.utils.TestUtils; import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; @@ -242,6 +241,6 @@ public void getCameraBoundaries_shouldReturnSensorInfoPixelArraySizeWhenRunningP } private static void updateSdkVersion(int version) { - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", version); + SdkCapabilityChecker.SDK_VERSION = version; } } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index c7b5b768783..db05691ada4 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -150,7 +150,7 @@ public void before() { @After public void after() { - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 0); + SdkCapabilityChecker.SDK_VERSION = 0; mockHandlerThreadFactory.close(); mockHandlerFactory.close(); } @@ -540,7 +540,7 @@ public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 24); + SdkCapabilityChecker.SDK_VERSION = 24; camera.pauseVideoRecording(mockResult); @@ -552,7 +552,7 @@ public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { @Test public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThenN() { TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 23); + SdkCapabilityChecker.SDK_VERSION = 23; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); camera.pauseVideoRecording(mockResult); @@ -568,7 +568,7 @@ public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCo MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 24); + SdkCapabilityChecker.SDK_VERSION = 24; IllegalStateException expectedException = new IllegalStateException("Test error message"); @@ -599,7 +599,7 @@ public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 24); + SdkCapabilityChecker.SDK_VERSION = 24; camera.resumeVideoRecording(mockResult); @@ -609,27 +609,40 @@ public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { } @Test - public void setDescriptionWhileRecording() { + public void setDescriptionWhileRecording_errorsWhenUnsupported() { MethodChannel.Result mockResult = mock(MethodChannel.Result.class); MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); TestUtils.setPrivateField(camera, "recordingVideo", true); TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); + SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.LOLLIPOP; final CameraProperties newCameraProperties = mock(CameraProperties.class); camera.setDescriptionWhileRecording(mockResult, newCameraProperties); - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { - verify(mockResult, times(1)) - .error( - eq("setDescriptionWhileRecordingFailed"), - eq("Device does not support switching the camera while recording"), - eq(null)); - } else { - verify(mockResult, times(1)).success(null); - verify(mockResult, never()).error(any(), any(), any()); - } + verify(mockResult, times(1)) + .error( + eq("setDescriptionWhileRecordingFailed"), + eq("Device does not support switching the camera while recording"), + eq(null)); + } + + @Test + public void setDescriptionWhileRecording_succeedsWhenSupported() { + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); + VideoRenderer mockVideoRenderer = mock(VideoRenderer.class); + TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); + TestUtils.setPrivateField(camera, "recordingVideo", true); + TestUtils.setPrivateField(camera, "videoRenderer", mockVideoRenderer); + SdkCapabilityChecker.SDK_VERSION = Build.VERSION_CODES.O; + + final CameraProperties newCameraProperties = mock(CameraProperties.class); + camera.setDescriptionWhileRecording(mockResult, newCameraProperties); + + verify(mockResult, times(1)).success(null); + verify(mockResult, never()).error(any(), any(), any()); } @Test @@ -767,7 +780,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 23); + SdkCapabilityChecker.SDK_VERSION = 23; MethodChannel.Result mockResult = mock(MethodChannel.Result.class); @@ -784,7 +797,7 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); TestUtils.setPrivateField(camera, "mediaRecorder", mockMediaRecorder); TestUtils.setPrivateField(camera, "recordingVideo", true); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", 24); + SdkCapabilityChecker.SDK_VERSION = 24; IllegalStateException expectedException = new IllegalStateException("Test error message"); diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java index b89aad0f677..93f81c6c153 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java @@ -15,9 +15,8 @@ import static org.mockito.Mockito.when; import android.hardware.camera2.CaptureRequest; -import android.os.Build.VERSION; import io.flutter.plugins.camera.CameraProperties; -import io.flutter.plugins.camera.utils.TestUtils; +import io.flutter.plugins.camera.SdkCapabilityChecker; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -25,15 +24,15 @@ public class NoiseReductionFeatureTest { @Before public void before() { - // Make sure the VERSION.SDK_INT field returns 23, to allow using all available + // Make sure the SDK_VERSION field returns 23, to allow using all available // noise reduction modes in tests. - TestUtils.setFinalStatic(VERSION.class, "SDK_INT", 23); + SdkCapabilityChecker.SDK_VERSION = 23; } @After public void after() { - // Make sure we reset the VERSION.SDK_INT field to it's original value. - TestUtils.setFinalStatic(VERSION.class, "SDK_INT", 0); + // Make sure we reset the SDK_VERSION field to it's original value. + SdkCapabilityChecker.SDK_VERSION = 0; } @Test diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java index 4d582696700..344f376ffad 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java @@ -20,8 +20,7 @@ import android.hardware.camera2.CaptureRequest; import android.os.Build; import io.flutter.plugins.camera.CameraProperties; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import io.flutter.plugins.camera.SdkCapabilityChecker; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -118,7 +117,7 @@ public void getDebugName_shouldReturnTheNameOfTheFeature() { public void getValue_shouldReturnNullIfNotSet() { ZoomLevelFeature zoomLevelFeature = new ZoomLevelFeature(mockCameraProperties); - assertEquals(1.0, (float) zoomLevelFeature.getValue(), 0); + assertEquals(1.0, zoomLevelFeature.getValue(), 0); } @Test @@ -127,7 +126,7 @@ public void getValue_shouldEchoSetValue() { zoomLevelFeature.setValue(2.3f); - assertEquals(2.3f, (float) zoomLevelFeature.getValue(), 0); + assertEquals(2.3f, zoomLevelFeature.getValue(), 0); } @Test @@ -209,11 +208,6 @@ public void checkZoomLevelFeature_callsScalarMaxZoomRatioOnAndroidR() throws Exc } static void setSdkVersion(int sdkVersion) throws Exception { - Field sdkInt = Build.VERSION.class.getField("SDK_INT"); - sdkInt.setAccessible(true); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(sdkInt, sdkInt.getModifiers() & ~Modifier.FINAL); - sdkInt.set(null, sdkVersion); + SdkCapabilityChecker.SDK_VERSION = sdkVersion; } } From 49e28f53848470cbf3eae9daad13197f5dc86fd2 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 14:34:57 -0400 Subject: [PATCH 10/22] Replace the remaining static access --- .../plugins/camera/CameraCaptureCallback.java | 12 ++++++++-- .../io/flutter/plugins/camera/DeviceInfo.java | 24 +++++++++++++++++++ .../plugins/camera/SdkCapabilityChecker.java | 1 + .../features/fpsrange/FpsRangeFeature.java | 6 +++-- .../CameraCaptureCallbackStatesTest.java | 20 +++++----------- .../fpsrange/FpsRangeFeaturePixel4aTest.java | 7 +++--- .../fpsrange/FpsRangeFeatureTest.java | 11 ++++----- .../plugins/camera/utils/TestUtils.java | 15 ------------ 8 files changed, 53 insertions(+), 43 deletions(-) create mode 100644 packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DeviceInfo.java diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java index 805f1829895..4f5e43593e0 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java @@ -11,6 +11,7 @@ import android.hardware.camera2.TotalCaptureResult; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import io.flutter.plugins.camera.types.CameraCaptureProperties; import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; @@ -25,6 +26,13 @@ class CameraCaptureCallback extends CaptureCallback { private final CaptureTimeoutsWrapper captureTimeouts; private final CameraCaptureProperties captureProps; + // Lookup keys for state; overrideable for unit tests since Mockito can't mock them. + @VisibleForTesting @NonNull + CaptureResult.Key aeStateKey = CaptureResult.CONTROL_AE_STATE; + + @VisibleForTesting @NonNull + CaptureResult.Key afStateKey = CaptureResult.CONTROL_AE_STATE; + private CameraCaptureCallback( @NonNull CameraCaptureStateListener cameraStateListener, @NonNull CaptureTimeoutsWrapper captureTimeouts, @@ -69,8 +77,8 @@ public void setCameraState(@NonNull CameraState state) { } private void process(CaptureResult result) { - Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); - Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(aeStateKey); + Integer afState = result.get(afStateKey); // Update capture properties if (result instanceof TotalCaptureResult) { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DeviceInfo.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DeviceInfo.java new file mode 100644 index 00000000000..fb1e92a8a64 --- /dev/null +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DeviceInfo.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.camera; + +import android.os.Build; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +/** Wraps BUILD device info, allowing for overriding it in unit tests. */ +public class DeviceInfo { + @VisibleForTesting public static @Nullable String BRAND = Build.BRAND; + + @VisibleForTesting public static @Nullable String MODEL = Build.MODEL; + + public static @Nullable String getBrand() { + return BRAND; + } + + public static @Nullable String getModel() { + return MODEL; + } +} diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java index 0bed5181bd2..b9fc328b657 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java @@ -9,6 +9,7 @@ import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.VisibleForTesting; +/** Abstracts SDK version checks, and allows overriding them in unit tests. */ public class SdkCapabilityChecker { /** The current SDK version, overridable for testing. */ @SuppressLint("AnnotateVersionCheck") diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java index 1f3104bbf4c..408e7a16b56 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java @@ -6,11 +6,11 @@ import android.annotation.SuppressLint; import android.hardware.camera2.CaptureRequest; -import android.os.Build; import android.util.Range; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.plugins.camera.CameraProperties; +import io.flutter.plugins.camera.DeviceInfo; import io.flutter.plugins.camera.features.CameraFeature; /** @@ -55,7 +55,9 @@ public FpsRangeFeature(@NonNull CameraProperties cameraProperties) { } private boolean isPixel4A() { - return Build.BRAND.equals("google") && Build.MODEL.equals("Pixel 4a"); + String brand = DeviceInfo.getBrand(); + String model = DeviceInfo.getModel(); + return brand != null && brand.equals("google") && model != null && model.equals("Pixel 4a"); } @NonNull diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java index 934aff857ec..56f5af93aa1 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java @@ -20,7 +20,6 @@ import io.flutter.plugins.camera.types.CameraCaptureProperties; import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper; import io.flutter.plugins.camera.types.Timeout; -import io.flutter.plugins.camera.utils.TestUtils; import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; @@ -89,17 +88,13 @@ protected void setUp() throws Exception { when(mockCaptureTimeouts.getPreCaptureFocusing()).thenReturn(mockTimeout); when(mockCaptureTimeouts.getPreCaptureMetering()).thenReturn(mockTimeout); - Key mockAeStateKey = mock(Key.class); - Key mockAfStateKey = mock(Key.class); - - TestUtils.setFinalStatic(CaptureResult.class, "CONTROL_AE_STATE", mockAeStateKey); - TestUtils.setFinalStatic(CaptureResult.class, "CONTROL_AF_STATE", mockAfStateKey); - mockedStaticTimeout.when(() -> Timeout.create(1000)).thenReturn(mockTimeout); cameraCaptureCallback = CameraCaptureCallback.create( mockCaptureStateListener, mockCaptureTimeouts, mockCaptureProps); + cameraCaptureCallback.aeStateKey = mock(Key.class); + cameraCaptureCallback.afStateKey = mock(Key.class); } @Override @@ -107,17 +102,14 @@ protected void tearDown() throws Exception { super.tearDown(); mockedStaticTimeout.close(); - - TestUtils.setFinalStatic(CaptureResult.class, "CONTROL_AE_STATE", null); - TestUtils.setFinalStatic(CaptureResult.class, "CONTROL_AF_STATE", null); } @Override protected void runTest() throws Throwable { - when(mockPartialCaptureResult.get(CaptureResult.CONTROL_AF_STATE)).thenReturn(afState); - when(mockPartialCaptureResult.get(CaptureResult.CONTROL_AE_STATE)).thenReturn(aeState); - when(mockTotalCaptureResult.get(CaptureResult.CONTROL_AF_STATE)).thenReturn(afState); - when(mockTotalCaptureResult.get(CaptureResult.CONTROL_AE_STATE)).thenReturn(aeState); + when(mockPartialCaptureResult.get(cameraCaptureCallback.afStateKey)).thenReturn(afState); + when(mockPartialCaptureResult.get(cameraCaptureCallback.aeStateKey)).thenReturn(aeState); + when(mockTotalCaptureResult.get(cameraCaptureCallback.afStateKey)).thenReturn(afState); + when(mockTotalCaptureResult.get(cameraCaptureCallback.aeStateKey)).thenReturn(aeState); cameraCaptureCallback.setCameraState(cameraState); if (isTimedOut) { diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java index 93cfe5523df..a7d981c69a2 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java @@ -7,10 +7,9 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -import android.os.Build; import android.util.Range; import io.flutter.plugins.camera.CameraProperties; -import io.flutter.plugins.camera.utils.TestUtils; +import io.flutter.plugins.camera.DeviceInfo; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -19,8 +18,8 @@ public class FpsRangeFeaturePixel4aTest { @Test public void ctor_shouldInitializeFpsRangeWith30WhenDeviceIsPixel4a() { - TestUtils.setFinalStatic(Build.class, "BRAND", "google"); - TestUtils.setFinalStatic(Build.class, "MODEL", "Pixel 4a"); + DeviceInfo.BRAND = "google"; + DeviceInfo.MODEL = "Pixel 4a"; FpsRangeFeature fpsRangeFeature = new FpsRangeFeature(mock(CameraProperties.class)); Range range = fpsRangeFeature.getValue(); diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java index ea67636e2ed..4dc68af94ca 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java @@ -13,10 +13,9 @@ import static org.mockito.Mockito.when; import android.hardware.camera2.CaptureRequest; -import android.os.Build; import android.util.Range; import io.flutter.plugins.camera.CameraProperties; -import io.flutter.plugins.camera.utils.TestUtils; +import io.flutter.plugins.camera.DeviceInfo; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -24,14 +23,14 @@ public class FpsRangeFeatureTest { @Before public void before() { - TestUtils.setFinalStatic(Build.class, "BRAND", "Test Brand"); - TestUtils.setFinalStatic(Build.class, "MODEL", "Test Model"); + DeviceInfo.BRAND = "Test Brand"; + DeviceInfo.MODEL = "Test Model"; } @After public void after() { - TestUtils.setFinalStatic(Build.class, "BRAND", null); - TestUtils.setFinalStatic(Build.class, "MODEL", null); + DeviceInfo.BRAND = null; + DeviceInfo.MODEL = null; } @Test diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java index fce99b54384..ddf5bcc8927 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java @@ -9,21 +9,6 @@ import org.junit.Assert; public class TestUtils { - public static void setFinalStatic(Class classToModify, String fieldName, Object newValue) { - try { - Field field = classToModify.getField(fieldName); - field.setAccessible(true); - - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - field.set(null, newValue); - } catch (Exception e) { - Assert.fail("Unable to mock static field: " + fieldName); - } - } - public static void setPrivateField(T instance, String fieldName, Object newValue) { try { Field field = instance.getClass().getDeclaredField(fieldName); From 205621b47a89c34e9c3249b1ef57da83cb256f17 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 14:53:07 -0400 Subject: [PATCH 11/22] Fix quick_actions tests --- .../quickactions/QuickActionsPlugin.java | 21 ++++++++++- .../quickactions/QuickActionsTest.java | 35 +++---------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java index 91950f3d799..bcb00d7de27 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java @@ -9,7 +9,9 @@ import android.content.Intent; import android.content.pm.ShortcutManager; import android.os.Build; +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -24,6 +26,23 @@ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewInte private MethodChannel channel; private MethodCallHandlerImpl handler; private Activity activity; + private final @NonNull AndroidCapabilityChecker capabilityChecker; + + // Interface for an injectable provider that returns the current SDK version. + @VisibleForTesting + interface AndroidCapabilityChecker { + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N_MR1) + boolean supportsShortcutManager(); + } + + public QuickActionsPlugin() { + this(() -> Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1); + } + + @VisibleForTesting + QuickActionsPlugin(@NonNull AndroidCapabilityChecker capabilityChecker) { + this.capabilityChecker = capabilityChecker; + } /** * Plugin registration. @@ -74,7 +93,7 @@ public void onDetachedFromActivityForConfigChanges() { @Override public boolean onNewIntent(@NonNull Intent intent) { // Do nothing for anything lower than API 25 as the functionality isn't supported. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { + if (!capabilityChecker.supportsShortcutManager()) { return false; } // Notify the Dart side if the launch intent has the intent extra relevant to quick actions. diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 911b789190a..36eb3eb7752 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -16,7 +16,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutManager; -import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding; @@ -25,9 +24,7 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.StandardMethodCodec; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.nio.ByteBuffer; -import org.junit.After; import org.junit.Test; public class QuickActionsTest { @@ -56,8 +53,6 @@ public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHa } } - static final int SUPPORTED_BUILD = 25; - static final int UNSUPPORTED_BUILD = 24; static final String SHORTCUT_TYPE = "action_one"; @Test @@ -75,9 +70,8 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() throws NoSuchFieldException, IllegalAccessException { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(); + final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> true); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); - setBuildVersion(SUPPORTED_BUILD); Field handler = plugin.getClass().getDeclaredField("handler"); handler.setAccessible(true); handler.set(plugin, mock(MethodCallHandlerImpl.class)); @@ -102,13 +96,11 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() } @Test - public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() - throws NoSuchFieldException, IllegalAccessException { + public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(); + final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> false); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); - setBuildVersion(UNSUPPORTED_BUILD); final Intent mockIntent = createMockIntentWithQuickActionExtra(); // Act @@ -120,13 +112,11 @@ public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() } @Test - public void onNewIntent_buildVersionSupported_invokesLaunchMethod() - throws NoSuchFieldException, IllegalAccessException { + public void onNewIntent_buildVersionSupported_invokesLaunchMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(); + final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> true); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); - setBuildVersion(SUPPORTED_BUILD); final Intent mockIntent = createMockIntentWithQuickActionExtra(); final Activity mockMainActivity = mock(Activity.class); when(mockMainActivity.getIntent()).thenReturn(mockIntent); @@ -161,19 +151,4 @@ private Intent createMockIntentWithQuickActionExtra() { when(mockIntent.getStringExtra(EXTRA_ACTION)).thenReturn(QuickActionsTest.SHORTCUT_TYPE); return mockIntent; } - - private void setBuildVersion(int buildVersion) - throws NoSuchFieldException, IllegalAccessException { - Field buildSdkField = Build.VERSION.class.getField("SDK_INT"); - buildSdkField.setAccessible(true); - final Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(buildSdkField, buildSdkField.getModifiers() & ~Modifier.FINAL); - buildSdkField.set(null, buildVersion); - } - - @After - public void tearDown() throws NoSuchFieldException, IllegalAccessException { - setBuildVersion(0); - } } From a0786b95ccc923edca239dfee5e1d2bded0521ac Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 17:04:33 -0400 Subject: [PATCH 12/22] webview WIP --- .../CookieManagerHostApiImpl.java | 20 ++++++++++++++ .../webviewflutter/utils/TestUtils.java | 26 ------------------- 2 files changed, 20 insertions(+), 26 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java index 6fdaeeaded0..bbb3a055d94 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java @@ -6,6 +6,8 @@ import android.os.Build; import android.webkit.CookieManager; + +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import io.flutter.plugin.common.BinaryMessenger; @@ -25,6 +27,14 @@ public class CookieManagerHostApiImpl implements CookieManagerHostApi { private final InstanceManager instanceManager; private final CookieManagerProxy proxy; + private final @NonNull AndroidSdkChecker sdkChecker; + + // Interface for an injectable SDK version checker. + @VisibleForTesting + interface AndroidSdkChecker { + @ChecksSdkIntAtLeast(parameter = 0) + boolean sdkIsAtLeast(int version); + } /** Proxy for constructors and static method of `CookieManager`. */ @VisibleForTesting @@ -58,9 +68,19 @@ public CookieManagerHostApiImpl( @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager, @NonNull CookieManagerProxy proxy) { + this(binaryMessenger, instanceManager, proxy, (int version) -> Build.VERSION.SDK_INT >= version); + } + + @VisibleForTesting + CookieManagerHostApiImpl( + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull CookieManagerProxy proxy, + @NonNull AndroidSdkChecker sdkChecker) { this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; this.proxy = proxy; + this.sdkChecker = sdkChecker; } @Override diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java deleted file mode 100644 index deebc417564..00000000000 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java +++ /dev/null @@ -1,26 +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.webviewflutter.utils; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import org.junit.Assert; - -public class TestUtils { - public static void setFinalStatic(Class classToModify, String fieldName, Object newValue) { - try { - Field field = classToModify.getField(fieldName); - field.setAccessible(true); - - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - field.set(null, newValue); - } catch (Exception e) { - Assert.fail("Unable to mock static field: " + fieldName); - } - } -} From a6fe405a6065198798a30d68c3cd0f4ec281287c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 20:03:39 -0400 Subject: [PATCH 13/22] Fix webview tests --- .../CookieManagerHostApiImpl.java | 30 ++++++++----------- .../webviewflutter/WebViewHostApiImpl.java | 26 +++++++++++++++- .../webviewflutter/CookieManagerTest.java | 17 ++++++----- .../plugins/webviewflutter/WebViewTest.java | 8 +++-- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java index bbb3a055d94..51ecfcb9759 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java @@ -6,7 +6,6 @@ import android.os.Build; import android.webkit.CookieManager; - import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -29,8 +28,8 @@ public class CookieManagerHostApiImpl implements CookieManagerHostApi { private final CookieManagerProxy proxy; private final @NonNull AndroidSdkChecker sdkChecker; - // Interface for an injectable SDK version checker. - @VisibleForTesting + // Interface for an injectable SDK version checker. + @VisibleForTesting interface AndroidSdkChecker { @ChecksSdkIntAtLeast(parameter = 0) boolean sdkIsAtLeast(int version); @@ -57,26 +56,21 @@ public CookieManagerHostApiImpl( this(binaryMessenger, instanceManager, new CookieManagerProxy()); } - /** - * Constructs a {@link CookieManagerHostApiImpl}. - * - * @param binaryMessenger used to communicate with Dart over asynchronous messages - * @param instanceManager maintains instances stored to communicate with attached Dart objects - * @param proxy proxy for constructors and static methods of `CookieManager` - */ - public CookieManagerHostApiImpl( + @VisibleForTesting + CookieManagerHostApiImpl( @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager, @NonNull CookieManagerProxy proxy) { - this(binaryMessenger, instanceManager, proxy, (int version) -> Build.VERSION.SDK_INT >= version); + this( + binaryMessenger, instanceManager, proxy, (int version) -> Build.VERSION.SDK_INT >= version); } @VisibleForTesting CookieManagerHostApiImpl( - @NonNull BinaryMessenger binaryMessenger, - @NonNull InstanceManager instanceManager, - @NonNull CookieManagerProxy proxy, - @NonNull AndroidSdkChecker sdkChecker) { + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull CookieManagerProxy proxy, + @NonNull AndroidSdkChecker sdkChecker) { this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; this.proxy = proxy; @@ -96,7 +90,7 @@ public void setCookie(@NonNull Long identifier, @NonNull String url, @NonNull St @Override public void removeAllCookies( @NonNull Long identifier, @NonNull GeneratedAndroidWebView.Result result) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.LOLLIPOP)) { getCookieManagerInstance(identifier).removeAllCookies(result::success); } else { result.success(removeCookiesPreL(getCookieManagerInstance(identifier))); @@ -106,7 +100,7 @@ public void removeAllCookies( @Override public void setAcceptThirdPartyCookies( @NonNull Long identifier, @NonNull Long webViewIdentifier, @NonNull Boolean accept) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.LOLLIPOP)) { getCookieManagerInstance(identifier) .setAcceptThirdPartyCookies( Objects.requireNonNull(instanceManager.getInstance(webViewIdentifier)), accept); 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 14c2b3db5d6..a0aa51d28ca 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 @@ -13,6 +13,7 @@ import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; +import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -74,6 +75,15 @@ public static class WebViewPlatformView extends WebView implements PlatformView private WebViewClient currentWebViewClient; private WebChromeClientHostApiImpl.SecureWebChromeClient currentWebChromeClient; + private final @NonNull AndroidSdkChecker sdkChecker; + + // Interface for an injectable SDK version checker. + @VisibleForTesting + interface AndroidSdkChecker { + @ChecksSdkIntAtLeast(parameter = 0) + boolean sdkIsAtLeast(int version); + } + /** * Creates a {@link WebViewPlatformView}. * @@ -83,10 +93,24 @@ public WebViewPlatformView( @NonNull Context context, @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) { + this( + context, + binaryMessenger, + instanceManager, + (int version) -> Build.VERSION.SDK_INT >= version); + } + + @VisibleForTesting + WebViewPlatformView( + @NonNull Context context, + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull AndroidSdkChecker sdkChecker) { super(context); currentWebViewClient = new WebViewClient(); currentWebChromeClient = new WebChromeClientHostApiImpl.SecureWebChromeClient(); api = new WebViewFlutterApiImpl(binaryMessenger, instanceManager); + this.sdkChecker = sdkChecker; setWebViewClient(currentWebViewClient); setWebChromeClient(currentWebChromeClient); @@ -108,7 +132,7 @@ public void dispose() {} @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.O)) { final FlutterView flutterView = tryFindFlutterView(); if (flutterView != null) { flutterView.setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerTest.java index 5c1abd19991..b2b52d843fa 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerTest.java @@ -15,7 +15,6 @@ import android.webkit.WebView; import androidx.annotation.NonNull; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugins.webviewflutter.utils.TestUtils; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -75,13 +74,15 @@ public void setCookie() { @SuppressWarnings({"rawtypes", "unchecked"}) @Test public void clearCookies() { - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); - final long instanceIdentifier = 0; instanceManager.addDartCreatedInstance(mockCookieManager, instanceIdentifier); final CookieManagerHostApiImpl hostApi = - new CookieManagerHostApiImpl(mockBinaryMessenger, instanceManager); + new CookieManagerHostApiImpl( + mockBinaryMessenger, + instanceManager, + new CookieManagerHostApiImpl.CookieManagerProxy(), + (int version) -> version <= Build.VERSION_CODES.LOLLIPOP); final Boolean[] successResult = new Boolean[1]; hostApi.removeAllCookies( @@ -108,8 +109,6 @@ public void error(@NonNull Throwable error) {} @Test public void setAcceptThirdPartyCookies() { - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); - final WebView mockWebView = mock(WebView.class); final long webViewIdentifier = 4; instanceManager.addDartCreatedInstance(mockWebView, webViewIdentifier); @@ -120,7 +119,11 @@ public void setAcceptThirdPartyCookies() { instanceManager.addDartCreatedInstance(mockCookieManager, instanceIdentifier); final CookieManagerHostApiImpl hostApi = - new CookieManagerHostApiImpl(mockBinaryMessenger, instanceManager); + new CookieManagerHostApiImpl( + mockBinaryMessenger, + instanceManager, + new CookieManagerHostApiImpl.CookieManagerProxy(), + (int version) -> version <= Build.VERSION_CODES.LOLLIPOP); hostApi.setAcceptThirdPartyCookies(instanceIdentifier, webViewIdentifier, accept); 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 00887275577..02b6d1fd7b7 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 @@ -26,7 +26,6 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewFlutterApi; import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView; -import io.flutter.plugins.webviewflutter.utils.TestUtils; import java.util.HashMap; import java.util.Objects; import org.junit.After; @@ -345,13 +344,16 @@ public void flutterApiCreate() { @Test public void setImportantForAutofillForParentFlutterView() { final WebViewPlatformView webView = - new WebViewPlatformView(mockContext, mockBinaryMessenger, testInstanceManager); + new WebViewPlatformView( + mockContext, + mockBinaryMessenger, + testInstanceManager, + (int version) -> version <= Build.VERSION_CODES.O); final WebViewPlatformView webViewSpy = spy(webView); final FlutterView mockFlutterView = mock(FlutterView.class); when(webViewSpy.getParent()).thenReturn(mockFlutterView); - TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.O); webViewSpy.onAttachedToWindow(); verify(mockFlutterView).setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES); From 650281fb11d3b8afbf734192590aea66eec3ba5c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 20:24:00 -0400 Subject: [PATCH 14/22] Rework previous versions to have more code within the tested path --- .../FileSelectorApiImpl.java | 27 ++++++------ .../FileSelectorAndroidPluginTest.java | 43 +++++++++++++++++-- .../quickactions/QuickActionsPlugin.java | 18 ++++---- .../quickactions/QuickActionsTest.java | 8 ++-- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java index 31178fb992e..19d9d973d5a 100644 --- a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java +++ b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java @@ -15,7 +15,6 @@ import android.provider.OpenableColumns; import android.util.Log; import android.webkit.MimeTypeMap; - import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -41,7 +40,7 @@ public class FileSelectorApiImpl implements GeneratedFileSelectorApi.FileSelecto private static final int OPEN_DIR = 223; private final @NonNull NativeObjectFactory objectFactory; - private final @NonNull AndroidCapabilityChecker capabilityChecker; + private final @NonNull AndroidSdkChecker sdkChecker; @Nullable ActivityPluginBinding activityPluginBinding; private abstract static class OnResultListener { @@ -63,25 +62,28 @@ DataInputStream newDataInputStream(InputStream inputStream) { } } - // Interface for an injectable provider that returns the current SDK version. + // Interface for an injectable SDK version checker. @VisibleForTesting - interface AndroidCapabilityChecker { - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.LOLLIPOP) - boolean supportsDirectorySelection(); + interface AndroidSdkChecker { + @ChecksSdkIntAtLeast(parameter = 0) + boolean sdkIsAtLeast(int version); } public FileSelectorApiImpl(@NonNull ActivityPluginBinding activityPluginBinding) { - this(activityPluginBinding, new NativeObjectFactory(), () -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); + this( + activityPluginBinding, + new NativeObjectFactory(), + (int version) -> Build.VERSION.SDK_INT >= version); } @VisibleForTesting FileSelectorApiImpl( @NonNull ActivityPluginBinding activityPluginBinding, @NonNull NativeObjectFactory objectFactory, - @NonNull AndroidCapabilityChecker capabilityChecker) { + @NonNull AndroidSdkChecker sdkChecker) { this.activityPluginBinding = activityPluginBinding; this.objectFactory = objectFactory; - this.capabilityChecker = capabilityChecker; + this.sdkChecker = sdkChecker; } @Override @@ -183,9 +185,10 @@ public void onResult(int resultCode, @Nullable Intent data) { @Override public void getDirectoryPath( @Nullable String initialDirectory, @NonNull GeneratedFileSelectorApi.Result result) { - if (!capabilityChecker.supportsDirectorySelection()) { - throw new UnsupportedOperationException( - "Selecting a directory is only supported on versions >= 21"); + if (!sdkChecker.sdkIsAtLeast(android.os.Build.VERSION_CODES.LOLLIPOP)) { + result.error( + new UnsupportedOperationException( + "Selecting a directory is only supported on versions >= 21")); } final Intent intent = objectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE); diff --git a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java index 8003c586b72..f242cf9bcbc 100644 --- a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java +++ b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java @@ -16,6 +16,7 @@ import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.provider.OpenableColumns; import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -79,7 +80,8 @@ public void openFileReturnsSuccessfully() throws FileNotFoundException { when(mockActivity.getContentResolver()).thenReturn(mockContentResolver); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); + new FileSelectorApiImpl( + mockActivityBinding, mockObjectFactory, (version) -> Build.VERSION.SDK_INT >= version); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.openFile( @@ -131,7 +133,8 @@ public void openFilesReturnsSuccessfully() throws FileNotFoundException { when(mockActivity.getContentResolver()).thenReturn(mockContentResolver); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); + new FileSelectorApiImpl( + mockActivityBinding, mockObjectFactory, (version) -> Build.VERSION.SDK_INT >= version); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.openFiles( @@ -192,7 +195,10 @@ public void getDirectoryPathReturnsSuccessfully() { when(mockObjectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE)).thenReturn(mockIntent); when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = - new FileSelectorApiImpl(mockActivityBinding, mockObjectFactory, () -> true); + new FileSelectorApiImpl( + mockActivityBinding, + mockObjectFactory, + (version) -> Build.VERSION_CODES.LOLLIPOP >= version); final GeneratedFileSelectorApi.Result mockResult = mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.getDirectoryPath(null, mockResult); @@ -209,4 +215,35 @@ public void getDirectoryPathReturnsSuccessfully() { verify(mockResult).success("some/path/"); } + + @Test + public void getDirectoryPath_errorsForUnsupportedVersion() { + final Uri mockUri = mock(Uri.class); + when(mockUri.toString()).thenReturn("some/path/"); + + when(mockObjectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE)).thenReturn(mockIntent); + when(mockActivityBinding.getActivity()).thenReturn(mockActivity); + final FileSelectorApiImpl fileSelectorApi = + new FileSelectorApiImpl( + mockActivityBinding, + mockObjectFactory, + (version) -> Build.VERSION_CODES.KITKAT >= version); + + @SuppressWarnings("unchecked") + final GeneratedFileSelectorApi.Result mockResult = + mock(GeneratedFileSelectorApi.Result.class); + fileSelectorApi.getDirectoryPath(null, mockResult); + + verify(mockActivity).startActivityForResult(mockIntent, 223); + + final ArgumentCaptor listenerArgumentCaptor = + ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); + verify(mockActivityBinding).addActivityResultListener(listenerArgumentCaptor.capture()); + + final Intent resultMockIntent = mock(Intent.class); + when(resultMockIntent.getData()).thenReturn(mockUri); + listenerArgumentCaptor.getValue().onActivityResult(223, Activity.RESULT_OK, resultMockIntent); + + verify(mockResult).error(any()); + } } diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java index bcb00d7de27..b1f78447afa 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java @@ -26,22 +26,22 @@ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewInte private MethodChannel channel; private MethodCallHandlerImpl handler; private Activity activity; - private final @NonNull AndroidCapabilityChecker capabilityChecker; + private final @NonNull AndroidSdkChecker sdkChecker; - // Interface for an injectable provider that returns the current SDK version. + // Interface for an injectable SDK version checker. @VisibleForTesting - interface AndroidCapabilityChecker { - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N_MR1) - boolean supportsShortcutManager(); + interface AndroidSdkChecker { + @ChecksSdkIntAtLeast(parameter = 0) + boolean sdkIsAtLeast(int version); } public QuickActionsPlugin() { - this(() -> Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1); + this((int version) -> Build.VERSION.SDK_INT >= version); } @VisibleForTesting - QuickActionsPlugin(@NonNull AndroidCapabilityChecker capabilityChecker) { - this.capabilityChecker = capabilityChecker; + QuickActionsPlugin(@NonNull AndroidSdkChecker capabilityChecker) { + this.sdkChecker = capabilityChecker; } /** @@ -93,7 +93,7 @@ public void onDetachedFromActivityForConfigChanges() { @Override public boolean onNewIntent(@NonNull Intent intent) { // Do nothing for anything lower than API 25 as the functionality isn't supported. - if (!capabilityChecker.supportsShortcutManager()) { + if (!sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.N_MR1)) { return false; } // Notify the Dart side if the launch intent has the intent extra relevant to quick actions. diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 36eb3eb7752..25d153c9520 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -53,6 +53,8 @@ public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHa } } + static final int SUPPORTED_BUILD = 25; + static final int UNSUPPORTED_BUILD = 24; static final String SHORTCUT_TYPE = "action_one"; @Test @@ -70,7 +72,7 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() throws NoSuchFieldException, IllegalAccessException { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> true); + final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); Field handler = plugin.getClass().getDeclaredField("handler"); handler.setAccessible(true); @@ -99,7 +101,7 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> false); + final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> UNSUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); final Intent mockIntent = createMockIntentWithQuickActionExtra(); @@ -115,7 +117,7 @@ public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() { public void onNewIntent_buildVersionSupported_invokesLaunchMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin(() -> true); + final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); final Intent mockIntent = createMockIntentWithQuickActionExtra(); final Activity mockMainActivity = mock(Activity.class); From b4f513a7e5b47b5c996bf0e53b3947fb45753b67 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 12 Jul 2023 20:26:10 -0400 Subject: [PATCH 15/22] Missed formatting --- .../java/io/flutter/plugins/camera/utils/TestUtils.java | 1 - .../flutter/plugins/quickactions/QuickActionsTest.java | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java index ddf5bcc8927..79911a0b0b5 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java @@ -5,7 +5,6 @@ package io.flutter.plugins.camera.utils; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import org.junit.Assert; public class TestUtils { diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 25d153c9520..ebee86645f6 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -72,7 +72,8 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() throws NoSuchFieldException, IllegalAccessException { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); + final QuickActionsPlugin plugin = + new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); Field handler = plugin.getClass().getDeclaredField("handler"); handler.setAccessible(true); @@ -101,7 +102,8 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> UNSUPPORTED_BUILD >= version); + final QuickActionsPlugin plugin = + new QuickActionsPlugin((version) -> UNSUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); final Intent mockIntent = createMockIntentWithQuickActionExtra(); @@ -117,7 +119,8 @@ public void onNewIntent_buildVersionUnsupported_doesNotInvokeMethod() { public void onNewIntent_buildVersionSupported_invokesLaunchMethod() { // Arrange final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); - final QuickActionsPlugin plugin = new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); + final QuickActionsPlugin plugin = + new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); final Intent mockIntent = createMockIntentWithQuickActionExtra(); final Activity mockMainActivity = mock(Activity.class); From c128a323cfc4dfe503e2079da2f3f31b95c5135a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 12 Jul 2023 21:42:21 -0400 Subject: [PATCH 16/22] Add missing return --- .../packages/file_selector_android/FileSelectorApiImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java index 19d9d973d5a..32502ed2693 100644 --- a/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java +++ b/packages/file_selector/file_selector_android/android/src/main/java/dev/flutter/packages/file_selector_android/FileSelectorApiImpl.java @@ -189,6 +189,7 @@ public void getDirectoryPath( result.error( new UnsupportedOperationException( "Selecting a directory is only supported on versions >= 21")); + return; } final Intent intent = objectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE); From ae5e9fbd81d024ba4a5585c044b9a4c5c60af23d Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 13 Jul 2023 06:58:12 -0400 Subject: [PATCH 17/22] Fix the new file_selector test --- .../FileSelectorAndroidPluginTest.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java index f242cf9bcbc..63dad80954a 100644 --- a/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java +++ b/packages/file_selector/file_selector_android/android/src/test/java/dev/flutter/packages/file_selector_android/FileSelectorAndroidPluginTest.java @@ -218,11 +218,6 @@ public void getDirectoryPathReturnsSuccessfully() { @Test public void getDirectoryPath_errorsForUnsupportedVersion() { - final Uri mockUri = mock(Uri.class); - when(mockUri.toString()).thenReturn("some/path/"); - - when(mockObjectFactory.newIntent(Intent.ACTION_OPEN_DOCUMENT_TREE)).thenReturn(mockIntent); - when(mockActivityBinding.getActivity()).thenReturn(mockActivity); final FileSelectorApiImpl fileSelectorApi = new FileSelectorApiImpl( mockActivityBinding, @@ -234,16 +229,6 @@ public void getDirectoryPath_errorsForUnsupportedVersion() { mock(GeneratedFileSelectorApi.Result.class); fileSelectorApi.getDirectoryPath(null, mockResult); - verify(mockActivity).startActivityForResult(mockIntent, 223); - - final ArgumentCaptor listenerArgumentCaptor = - ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); - verify(mockActivityBinding).addActivityResultListener(listenerArgumentCaptor.capture()); - - final Intent resultMockIntent = mock(Intent.class); - when(resultMockIntent.getData()).thenReturn(mockUri); - listenerArgumentCaptor.getValue().onActivityResult(223, Activity.RESULT_OK, resultMockIntent); - verify(mockResult).error(any()); } } From ab00422cfe85764c523ada1eabb640c98fd95468 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 13 Jul 2023 07:00:39 -0400 Subject: [PATCH 18/22] Bump versions --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/pubspec.yaml | 2 +- packages/file_selector/file_selector_android/CHANGELOG.md | 4 ++++ packages/file_selector/file_selector_android/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_android/CHANGELOG.md | 4 ++++ packages/quick_actions/quick_actions_android/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 6c6746414ef..a0a0fd8ae6b 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.8+4 + +* Adjusts SDK checks for better testability. + ## 0.10.8+3 * Fixes unawaited_futures violations. diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 4a2703ce077..307506a42f9 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.8+3 +version: 0.10.8+4 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/file_selector/file_selector_android/CHANGELOG.md b/packages/file_selector/file_selector_android/CHANGELOG.md index 4499364cd4e..2da6f23dc8a 100644 --- a/packages/file_selector/file_selector_android/CHANGELOG.md +++ b/packages/file_selector/file_selector_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+2 + +* Adjusts SDK checks for better testability. + ## 0.5.0+1 * Bumps androidx.annotation:annotation from 1.5.0 to 1.6.0. diff --git a/packages/file_selector/file_selector_android/pubspec.yaml b/packages/file_selector/file_selector_android/pubspec.yaml index cf92e2719b9..18ed625ba7b 100644 --- a/packages/file_selector/file_selector_android/pubspec.yaml +++ b/packages/file_selector/file_selector_android/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_android description: Android implementation of the file_selector package. repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.5.0+1 +version: 0.5.0+2 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index c1a07dcda76..0207791c36b 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.7 + +* Adjusts SDK checks for better testability. + ## 1.0.6 * Removes obsolete null checks on non-nullable values. diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 04884c40e4a..0ef46b29d46 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.0.6 +version: 1.0.7 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 114f2a74eb7..90cb8636cc1 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.8.3 + +* Adjusts SDK checks for better testability. + ## 3.8.2 * Fixes unawaited_futures violations. diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a58c031b82b..0621731cd3c 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/packages/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: 3.8.2 +version: 3.8.3 environment: sdk: ">=2.18.0 <4.0.0" From 9975a899ec0b544cbc02d74bd8cf3e7628568615 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 14 Jul 2023 21:48:21 -0400 Subject: [PATCH 19/22] Add comments Co-authored-by: Reid Baker --- .../io/flutter/plugins/camera/SdkCapabilityChecker.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java index b9fc328b657..11a1ee05fff 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java @@ -18,6 +18,7 @@ public class SdkCapabilityChecker { @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) public static boolean supportsDistortionCorrection() { + // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES return SDK_VERSION >= Build.VERSION_CODES.P; } @@ -28,26 +29,31 @@ public static boolean supportsEglRecordableAndroid() { @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) public static boolean supportsEncoderProfiles() { + // See https://developer.android.com/reference/android/media/EncoderProfiles return SDK_VERSION >= Build.VERSION_CODES.S; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) public static boolean supportsMarshmallowNoiseReductionModes() { + // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES return SDK_VERSION >= Build.VERSION_CODES.M; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) public static boolean supportsSessionConfiguration() { + // See https://developer.android.com/reference/android/hardware/camera2/params/SessionConfiguration return SDK_VERSION >= Build.VERSION_CODES.P; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) public static boolean supportsVideoPause() { + // See https://developer.android.com/reference/androidx/camera/video/VideoRecordEvent.Pause return SDK_VERSION >= Build.VERSION_CODES.N; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) public static boolean supportsZoomRatio() { + // See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest#CONTROL_ZOOM_RATIO return SDK_VERSION >= Build.VERSION_CODES.R; } } From 529d44eaf3db0a88221149e4495c8a605ba5658b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 14 Jul 2023 21:51:32 -0400 Subject: [PATCH 20/22] Fix indentation, add one more docs reference --- .../plugins/camera/SdkCapabilityChecker.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java index 11a1ee05fff..1ff512672d9 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java @@ -18,42 +18,43 @@ public class SdkCapabilityChecker { @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) public static boolean supportsDistortionCorrection() { - // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES + // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES return SDK_VERSION >= Build.VERSION_CODES.P; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) public static boolean supportsEglRecordableAndroid() { + // See https://developer.android.com/reference/android/opengl/EGLExt#EGL_RECORDABLE_ANDROID return SDK_VERSION >= Build.VERSION_CODES.O; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) public static boolean supportsEncoderProfiles() { - // See https://developer.android.com/reference/android/media/EncoderProfiles + // See https://developer.android.com/reference/android/media/EncoderProfiles return SDK_VERSION >= Build.VERSION_CODES.S; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) public static boolean supportsMarshmallowNoiseReductionModes() { - // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES + // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES return SDK_VERSION >= Build.VERSION_CODES.M; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) public static boolean supportsSessionConfiguration() { - // See https://developer.android.com/reference/android/hardware/camera2/params/SessionConfiguration + // See https://developer.android.com/reference/android/hardware/camera2/params/SessionConfiguration return SDK_VERSION >= Build.VERSION_CODES.P; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) public static boolean supportsVideoPause() { - // See https://developer.android.com/reference/androidx/camera/video/VideoRecordEvent.Pause + // See https://developer.android.com/reference/androidx/camera/video/VideoRecordEvent.Pause return SDK_VERSION >= Build.VERSION_CODES.N; } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) public static boolean supportsZoomRatio() { - // See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest#CONTROL_ZOOM_RATIO + // See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest#CONTROL_ZOOM_RATIO return SDK_VERSION >= Build.VERSION_CODES.R; } } From bf744082a8091e687ecf129515766b35248cb90c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 14 Jul 2023 21:52:37 -0400 Subject: [PATCH 21/22] Try JDK 17 --- .ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci.yaml b/.ci.yaml index 4ffaa5e4656..c46724748f7 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -26,7 +26,7 @@ platform_properties: dependencies: >- [ {"dependency": "android_sdk", "version": "version:33v6"}, - {"dependency": "open_jdk", "version": "version:12"}, + {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "curl", "version": "version:7.64.0"} ] linux_desktop: From 8a173457f0e777c8c445f18ac721c0ee2fb4f078 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 14 Jul 2023 22:30:35 -0400 Subject: [PATCH 22/22] Keep build-all-packages on JDK 11 --- .ci.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.ci.yaml b/.ci.yaml index c46724748f7..8702a476974 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -286,6 +286,11 @@ targets: version_file: flutter_master.version target_file: android_build_all_packages.yaml channel: master + # The legacy project build requires an older JDK. + dependencies: >- + [ + {"dependency": "open_jdk", "version": "version:11"} + ] - name: Linux_android android_build_all_packages stable recipe: packages/packages @@ -295,6 +300,11 @@ targets: version_file: flutter_stable.version target_file: android_build_all_packages.yaml channel: stable + # The legacy project build requires an older JDK. + dependencies: >- + [ + {"dependency": "open_jdk", "version": "version:11"} + ] - name: Linux_android android_platform_tests_shard_1 master recipe: packages/packages